温控驱动(四)msm-tsens (2)设置温控传感器的threshold

这篇博客我们结合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;
}

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值