这篇博客我们结合thermal-engine与驱动一起分析,如果设置温度传感器的threshold,以及thermal-engine的wait函数如何结束。
一、thermal-engine的流程
之前我们在博客 温控daemon(三)sensor初始化中分析过sensor监控温度的流程,以及在温控daemon(六)Monitor算法这两篇博客结合分析可以知道如何算法结合sensor控制温度。
这里我们再简单梳理下,每个sensor会创建一个thread来执行tsens_uevent函数,来通过poll监听thermal_zone下面的type是否有事件,如果有事件直接broadcast tsens->tsens_condition。
static void *tsens_uevent(void *data)
{
int err = 0;
struct sensor_info *sensor = (struct sensor_info *)data;
struct pollfd fds;
int fd;
char uevent[MAX_PATH] = {0};
char buf[MAX_PATH] = {0};
struct tsens_data *tsens = NULL;
if (NULL == sensor ||
NULL == sensor->data) {
msg("%s: unexpected NULL", __func__);
return NULL;
}
tsens = (struct tsens_data *) sensor->data;
/* Looking for tsens uevent */
snprintf(uevent, MAX_PATH, TZ_TYPE, sensor->tzn);
fd = open(uevent, O_RDONLY);
if (fd < 0) {
msg("Unable to open %s to receive notifications.\n", uevent);
return NULL;
};
while (!tsens->sensor_shutdown) {
fds.fd = fd;
fds.events = POLLERR|POLLPRI;
fds.revents = 0;
err = poll(&fds, 1, -1);
if (err == -1) {
msg("Error in uevent poll.\n");
break;
}
err = read(fd, buf, sizeof(buf));
if (err < 0)
msg("sysfs[%s] read error:%d\n", uevent, errno);
lseek(fd, 0, SEEK_SET);
dbgmsg("%s: %s", __func__, buf);
/* notify the waiting threads */
pthread_mutex_lock(&(tsens->tsens_mutex));
tsens->threshold_reached = 1;
pthread_cond_broadcast(&(tsens->tsens_condition));
pthread_mutex_unlock(&(tsens->tsens_mutex));
}
close(fd);
return NULL;
}
我们再来分析sensor还会创建另一个thread来执行sensor_monitor函数,这个函数会在sensor_wait中阻塞,当阻塞结束会调用notify_clnts来通知各个client来更新sensor的threshold等,最后再broadcast condition,使具体的算法主程序继续往下走。
static void *sensor_monitor(void *vsensor_mgr)
{
struct sensors_mgr_sensor_info *sensor_mgr = vsensor_mgr;
while (sensor_mgr->thread_shutdown != 1) {
/* Wait here until there is actually a request to process */
if (!sensor_mgr->req_active) {
dbgmsg("%s: %s Wait for client request.\n", __func__, sensor_mgr->name);
pthread_mutex_lock(&(sensor_mgr->req_wait_mutex));
while (!sensor_mgr->req_active) {
pthread_cond_wait(&(sensor_mgr->req_wait_cond),
&(sensor_mgr->req_wait_mutex));
}
pthread_mutex_unlock(&(sensor_mgr->req_wait_mutex));
}
dbgmsg("%s: %s Sensor wait.\n", __func__, sensor_mgr->name);
sensor_wait(sensor_mgr);
if (sensor_mgr->get_trip_temperature)
sensor_mgr->last_reading =
sensor_mgr->get_trip_temperature(sensor_mgr);
else
sensor_mgr->last_reading =
sensor_mgr->get_temperature(sensor_mgr);
dbgmsg("%s: %s Reading %d .\n", __func__, sensor_mgr->name,
sensor_mgr->last_reading);
if (!strncmp(sensor_mgr->name, "bcl", 3)) {
thermalmsg(LOG_LVL_DBG, (LOG_LOGCAT | LOG_LOCAL_SOCKET),
"%s:%s:%d mA\n", SENSORS,
sensor_mgr->name, sensor_mgr->last_reading);
} else if (!strncmp(sensor_mgr->name, "bcm", 3)) {
thermalmsg(LOG_LVL_DBG, (LOG_LOGCAT | LOG_LOCAL_SOCKET),
"%s:%s:%d mPercent\n", SENSORS,
sensor_mgr->name, sensor_mgr->last_reading);
} else {
thermalmsg(LOG_LVL_DBG, (LOG_LOGCAT | LOG_LOCAL_SOCKET),
"%s:%s:%d mC\n", SENSORS,
sensor_mgr->name, sensor_mgr->last_reading);
}
notify_clnts(sensor_mgr);
}
return NULL;
}
notify_clnts先会调用到各个算法的notify_cb_func函数,这个函数会去broadcast wait_cond,而这个时候thermal的算法的主线程正在wait这个,这样之后算法的主线程可以继续。
static void sensor_thresh_notify(sensor_clnt_handle clnt,
enum sensor_notify_event_type event,
int reading,
void *data)
{
if (NULL == clnt) {
msg("%s: unexpected NULL", __func__);
return;
}
if (((uintptr_t)data) >= tm_cnt) {
msg("%s: unexpected idx %zd", __func__, (uintptr_t)data);
return;
}
dbgmsg("%s: Update recieved %s %d", __func__,
tm_states[(uintptr_t)data].setting->desc,
reading);
/* Notify the waiting thread */
pthread_mutex_lock(&wait_mutex);
THRESH_MASK_SET((uintptr_t)data);
pthread_cond_broadcast(&wait_cond);
pthread_mutex_unlock(&wait_mutex);
}
sensor的sensor_monitor函数最后会调用到update_active_thresh函数,这个函数又会调用到sensor的set_thresholds函数,这个函数就是去设置sensor的threshold和使能。
static void set_thresholds(struct tsens_data *tsens, struct thresholds_req_t *thresh)
{
char minname[MAX_PATH]= {0};
char maxname[MAX_PATH]= {0};
char buf[LVL_BUF_MAX] = {0};
int ret = 0;
int mintemp = 0;
if (NULL == tsens) {
msg("%s: unexpected NULL", __func__);
return;
}
snprintf(minname, MAX_PATH, TSENS_TZ_TRIP_TEMP, tsens->sensor->tzn, tsens->trip_min);
snprintf(maxname, MAX_PATH, TSENS_TZ_TRIP_TEMP, tsens->sensor->tzn, tsens->trip_max);
/* Set thresholds in legal order */
if (read_line_from_file(minname, buf, sizeof(buf)) > 0) {
mintemp = atoi(buf);
}
if (thresh->high >= (mintemp / tsens->sensor->scaling_factor)) {
/* set high threshold first */
if (thresh->high_valid) {
dbgmsg("Setting up TSENS thresholds high: %d\n", thresh->high);
snprintf(buf, LVL_BUF_MAX, "%d", thresh->high * tsens->sensor->scaling_factor);
ret = write_to_file(maxname, buf, strlen(buf));
if (ret <= 0)
msg("TSENS threshold high failed to set %d\n", thresh->high);
}
enable_threshold(tsens, tsens->trip_max, thresh->high_valid);
if (thresh->low_valid) {
dbgmsg("Setting up TSENS thresholds low: %d\n", thresh->low);
snprintf(buf, LVL_BUF_MAX, "%d", thresh->low * tsens->sensor->scaling_factor);
ret = write_to_file(minname, buf, strlen(buf));
if (ret <= 0)
msg("TSENS threshold low failed to set %d\n", thresh->low);
}
enable_threshold(tsens, tsens->trip_min, thresh->low_valid);
} else {
if (thresh->low_valid) {
dbgmsg("Setting up TSENS thresholds low: %d\n", thresh->low);
snprintf(buf, LVL_BUF_MAX, "%d", thresh->low * tsens->sensor->scaling_factor);
ret = write_to_file(minname, buf, strlen(buf));
if (ret <= 0)
msg("TSENS threshold low failed to set %d\n", thresh->low);
}
enable_threshold(tsens, tsens->trip_min, thresh->low_valid);
if (thresh->high_valid) {
dbgmsg("Setting up TSENS thresholds high: %d\n", thresh->high);
snprintf(buf, LVL_BUF_MAX, "%d", thresh->high * tsens->sensor->scaling_factor);
ret = write_to_file(maxname, buf, strlen(buf));
if (ret <= 0)
msg("TSENS threshold high failed to set %d\n", thresh->high);
}
enable_threshold(tsens, tsens->trip_max, thresh->high_valid);
}
}
这样我们大致的流程就通了。首先sensor的一个thread执行tsens_uevent,这个线程一直在poll等待type节点有事件上来,有事件上来后,去broadcast是另一个thread sensor_monitor从sensor_wait 中唤醒然后执行notify_clnts函数,notify_clnts函数会去调用各个算法中的notify_cb_func函数,比如monitor算法的sensor_thresh_notify函数会去broadcast,然后monitor算法的主线程执行的sensor_monitor会唤醒继续去执行handle_thresh_sig函数处理算法的流程。在notify_clnts中还会调用update_active_thresh函数,然后调用比如msm_tsens的tsens_sensor_update_thresholds函数,这个函数会去设置sensor的threshold以及enabled。
在trip_point_0_temp,trip_point_0_type,trip_point_1_temp,trip_point_2_type这四个节点。temp用来设置温度,type用来使能。
二、驱动的流程
2.1 创建threshold节点
之前在温控驱动(三)msm-tsens博客中已经分析了msm_tsens的大致流程,这里我们在thermal_zone_device_register函数中会调用create_trip_attrs函数,会创建trip_point_0_temp,trip_point_0_type,trip_point_1_temp,trip_point_2_type这四个节点。
static int create_trip_attrs(struct thermal_zone_device *tz, int mask)
{
......
for (indx = 0; indx < tz->trips; indx++) {
/* create trip type attribute */
snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH,
"trip_point_%d_type", indx);
sysfs_attr_init(&tz->trip_type_attrs[indx].attr.attr);
tz->trip_type_attrs[indx].attr.attr.name =
tz->trip_type_attrs[indx].name;
tz->trip_type_attrs[indx].attr.attr.mode = S_IRUGO | S_IWUSR;
tz->trip_type_attrs[indx].attr.show = trip_point_type_show;
tz->trip_type_attrs[indx].attr.store = trip_point_type_activate;
device_create_file(&tz->device,
&tz->trip_type_attrs[indx].attr);
/* create trip temp attribute */
snprintf(tz->trip_temp_attrs[indx].name, THERMAL_NAME_LENGTH,
"trip_point_%d_temp", indx);
sysfs_attr_init(&tz->trip_temp_attrs[indx].attr.attr);
tz->trip_temp_attrs[indx].attr.attr.name =
tz->trip_temp_attrs[indx].name;
tz->trip_temp_attrs[indx].attr.attr.mode = S_IRUGO;
tz->trip_temp_attrs[indx].attr.show = trip_point_temp_show;
if (IS_ENABLED(CONFIG_THERMAL_WRITABLE_TRIPS) &&
mask & (1 << indx)) {
tz->trip_temp_attrs[indx].attr.attr.mode |= S_IWUSR;
tz->trip_temp_attrs[indx].attr.store =
trip_point_temp_store;
}
device_create_file(&tz->device,
&tz->trip_temp_attrs[indx].attr);
......
}
return 0;
}
我们先来看下下面两个节点分别是配置threshold的low还是high的。
msm8909go_benz:/sys/devices/virtual/thermal/thermal_zone3 # cat trip_point_1_type
at trip_point_1_type <
configurable_low
msm8909go_benz:/sys/devices/virtual/thermal/thermal_zone3 # cat trip_point_0_type
at trip_point_0_type <
configurable_hi
设置温度threshold trip_point_temp_store函数调用了sensor_set_trip_temp来设置
static ssize_t
trip_point_temp_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
int trip, ret;
long temperature;
if (!tz->ops->set_trip_temp)
return -EPERM;
if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
return -EINVAL;
if (kstrtol(buf, 10, &temperature))
return -EINVAL;
ret = sensor_set_trip_temp(tz, trip, temperature);
return ret ? ret : count;
}
而sensor_set_trip_temp主要就是把threshold对象的temp给修改了。
int sensor_set_trip_temp(struct thermal_zone_device *tz,
int trip, long temp)
{
int ret = 0;
struct sensor_threshold *threshold = NULL;
if (!tz->ops->get_trip_type)
return -EPERM;
get_trip_threshold(tz, trip, &threshold);//获取到threshold对象
if (threshold) {
threshold->temp = temp;//将threshold对象的temp修改
ret = sensor_set_trip(tz->sensor.sensor_id, threshold);//这里和同步相关的
} else {
ret = tz->ops->set_trip_temp(tz, trip, temp);
}
return ret;
}
下面我们来看另一个函数,这个函数是trip type的store函数,获取到threshold对象之后调用了sensor_activate_trip。
static ssize_t
trip_point_type_activate(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
int trip, result = 0;
bool activate;
struct sensor_threshold *threshold = NULL;
if (!tz->ops->get_trip_type ||
!tz->ops->activate_trip_type) {
result = -EPERM;
goto trip_activate_exit;
}
if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip)) {
result = -EINVAL;
goto trip_activate_exit;
}
if (!strcmp(buf, "enabled")) {
activate = true;
} else if (!strcmp(buf, "disabled")) {
activate = false;
} else {
result = -EINVAL;
goto trip_activate_exit;
}
get_trip_threshold(tz, trip, &threshold);
if (threshold)
result = sensor_activate_trip(tz->sensor.sensor_id,
threshold, activate);
else
result = tz->ops->activate_trip_type(tz, trip,
activate ? THERMAL_TRIP_ACTIVATION_ENABLED :
THERMAL_TRIP_ACTIVATION_DISABLED);
trip_activate_exit:
if (result)
return result;
return count;
}
sensor_activate_trip函数调用了__update_sensor_thresholds函数,而在这个函数中通过ops的set_trip_temp函数和activate_trip_type函数最终把threshold值,设置到硬件的寄存器中。当设置到寄存器之后,当温度大于high或者小于low都应该会有中断。
2.2 中断
在msm-tsens.c的tsens_thermal_zone_register函数中我们先调用了thermal_core.c的thermal_zone_device_register函数,然后就是调用了request_threaded_irq函数创建一个中断执行函数。而这个中断就是dts中的tsens-upper-lower,中断的执行函数就是tsens_irq_thread函数,这个函数会调用thermal_sensor_trip函数:
thermal_sensor_trip函数会去complete sysfs_notify_complete。
int thermal_sensor_trip(struct thermal_zone_device *tz,
enum thermal_trip_type trip, long temp)
{
struct sensor_threshold *pos = NULL;
int ret = -ENODEV;
if (trip != THERMAL_TRIP_CONFIGURABLE_HI &&
trip != THERMAL_TRIP_CONFIGURABLE_LOW)
return 0;
if (list_empty(&tz->sensor.threshold_list))
return 0;
rcu_read_lock();
list_for_each_entry_rcu(pos, &tz->sensor.threshold_list, list) {
if ((pos->trip != trip) || (!pos->active))
continue;
if (((trip == THERMAL_TRIP_CONFIGURABLE_LOW) &&
(pos->temp <= tz->sensor.threshold_min) &&
(pos->temp >= temp)) ||
((trip == THERMAL_TRIP_CONFIGURABLE_HI) &&
(pos->temp >= tz->sensor.threshold_max) &&
(pos->temp <= temp))) {
if ((pos == &tz->tz_threshold[0])
|| (pos == &tz->tz_threshold[1]))
complete(&tz->sensor.sysfs_notify_complete);
pos->active = 0;
pos->notify(trip, temp, pos->data);
}
}
rcu_read_unlock();
schedule_work(&tz->sensor.work);
return ret;
}
EXPORT_SYMBOL(thermal_sensor_trip);
我们再来看再thermal_core.c的thermal_zone_device_register函数会去调用sensor_init函数。sensor_init函数创建了一个thread执行sensor_sysfs_notify函数。
int sensor_init(struct thermal_zone_device *tz)
{
struct sensor_info *sensor = &tz->sensor;
sensor->sensor_id = tz->id;
sensor->tz = tz;
sensor->threshold_min = LONG_MIN;
sensor->threshold_max = LONG_MAX;
sensor->max_idx = -1;
sensor->min_idx = -1;
mutex_init(&sensor->lock);
INIT_LIST_HEAD_RCU(&sensor->sensor_list);
INIT_LIST_HEAD_RCU(&sensor->threshold_list);
INIT_LIST_HEAD(&tz->tz_threshold[0].list);
INIT_LIST_HEAD(&tz->tz_threshold[1].list);
tz->tz_threshold[0].notify = tz_notify_trip;
tz->tz_threshold[0].data = tz;
tz->tz_threshold[0].trip = THERMAL_TRIP_CONFIGURABLE_HI;
tz->tz_threshold[1].notify = tz_notify_trip;
tz->tz_threshold[1].data = tz;
tz->tz_threshold[1].trip = THERMAL_TRIP_CONFIGURABLE_LOW;
list_add_rcu(&sensor->sensor_list, &sensor_info_list);
INIT_WORK(&sensor->work, sensor_update_work);
init_completion(&sensor->sysfs_notify_complete);
sensor->sysfs_notify_thread = kthread_run(sensor_sysfs_notify,
&tz->sensor,
"therm_core:notify%d",
tz->id);
if (IS_ERR(sensor->sysfs_notify_thread))
pr_err("Failed to create notify thread %d", tz->id);
return 0;
}
sensor_sysfs_notify函数,会wait sys_notify_complete,唤醒后会调用sysfs_notify函数,而对象是device的kobj,THERMAL_UEVENT_DATA位type,也就是说这时候kernel会给thermal-engine的type节点发送uevent事件,这样前面说到的thermal-engine中的poll type节点就不会阻塞了。
static __ref int sensor_sysfs_notify(void *data)
{
int ret = 0;
struct sensor_info *sensor = (struct sensor_info *)data;
while (!kthread_should_stop()) {
while (wait_for_completion_interruptible(
&sensor->sysfs_notify_complete) != 0)
;
reinit_completion(&sensor->sysfs_notify_complete);
sysfs_notify(&sensor->tz->device.kobj, NULL,
THERMAL_UEVENT_DATA);
}
return ret;
}