Linux Audio

Check our new training course

Loading...
v6.8
  1// SPDX-License-Identifier: LGPL-2.1+
  2// Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org>
  3#define _GNU_SOURCE
  4#include <errno.h>
  5#include <stdio.h>
  6#include <stdlib.h>
  7#include <unistd.h>
 
  8
  9#include <thermal.h>
 10#include "thermal_nl.h"
 11
 12static struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = {
 13	/* Thermal zone */
 14	[THERMAL_GENL_ATTR_TZ]                  = { .type = NLA_NESTED },
 15	[THERMAL_GENL_ATTR_TZ_ID]               = { .type = NLA_U32 },
 16	[THERMAL_GENL_ATTR_TZ_TEMP]             = { .type = NLA_U32 },
 17	[THERMAL_GENL_ATTR_TZ_TRIP]             = { .type = NLA_NESTED },
 18	[THERMAL_GENL_ATTR_TZ_TRIP_ID]          = { .type = NLA_U32 },
 19	[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]        = { .type = NLA_U32 },
 20	[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]        = { .type = NLA_U32 },
 21	[THERMAL_GENL_ATTR_TZ_TRIP_HYST]        = { .type = NLA_U32 },
 22	[THERMAL_GENL_ATTR_TZ_MODE]             = { .type = NLA_U32 },
 23	[THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT]      = { .type = NLA_U32 },
 24	[THERMAL_GENL_ATTR_TZ_NAME]             = { .type = NLA_STRING },
 25
 26	/* Governor(s) */
 27	[THERMAL_GENL_ATTR_TZ_GOV]              = { .type = NLA_NESTED },
 28	[THERMAL_GENL_ATTR_TZ_GOV_NAME]         = { .type = NLA_STRING },
 29
 30	/* Cooling devices */
 31	[THERMAL_GENL_ATTR_CDEV]                = { .type = NLA_NESTED },
 32	[THERMAL_GENL_ATTR_CDEV_ID]             = { .type = NLA_U32 },
 33	[THERMAL_GENL_ATTR_CDEV_CUR_STATE]      = { .type = NLA_U32 },
 34	[THERMAL_GENL_ATTR_CDEV_MAX_STATE]      = { .type = NLA_U32 },
 35	[THERMAL_GENL_ATTR_CDEV_NAME]           = { .type = NLA_STRING },
 
 
 
 
 
 36};
 37
 38static int parse_tz_get(struct genl_info *info, struct thermal_zone **tz)
 39{
 40	struct nlattr *attr;
 41	struct thermal_zone *__tz = NULL;
 42	size_t size = 0;
 43	int rem;
 44
 45	nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_TZ], rem) {
 46
 47		if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_ID) {
 48
 49			size++;
 50
 51			__tz = realloc(__tz, sizeof(*__tz) * (size + 2));
 52			if (!__tz)
 53				return THERMAL_ERROR;
 54
 55			__tz[size - 1].id = nla_get_u32(attr);
 56		}
 57
 58
 59		if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_NAME)
 60			nla_strlcpy(__tz[size - 1].name, attr,
 61				    THERMAL_NAME_LENGTH);
 62	}
 63
 64	if (__tz)
 65		__tz[size].id = -1;
 66
 67	*tz = __tz;
 68
 69	return THERMAL_SUCCESS;
 70}
 71
 72static int parse_cdev_get(struct genl_info *info, struct thermal_cdev **cdev)
 73{
 74	struct nlattr *attr;
 75	struct thermal_cdev *__cdev = NULL;
 76	size_t size = 0;
 77	int rem;
 78
 79	nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_CDEV], rem) {
 80
 81		if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_ID) {
 82
 83			size++;
 84
 85			__cdev = realloc(__cdev, sizeof(*__cdev) * (size + 2));
 86			if (!__cdev)
 87				return THERMAL_ERROR;
 88
 89			__cdev[size - 1].id = nla_get_u32(attr);
 90		}
 91
 92		if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_NAME) {
 93			nla_strlcpy(__cdev[size - 1].name, attr,
 94				    THERMAL_NAME_LENGTH);
 95		}
 96
 97		if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_CUR_STATE)
 98			__cdev[size - 1].cur_state = nla_get_u32(attr);
 99
100		if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_MAX_STATE)
101			__cdev[size - 1].max_state = nla_get_u32(attr);
102	}
103
104	if (__cdev)
105		__cdev[size].id = -1;
106
107	*cdev = __cdev;
108
109	return THERMAL_SUCCESS;
110}
111
112static int parse_tz_get_trip(struct genl_info *info, struct thermal_zone *tz)
113{
114	struct nlattr *attr;
115	struct thermal_trip *__tt = NULL;
116	size_t size = 0;
117	int rem;
118
119	nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_TZ_TRIP], rem) {
120
121		if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_ID) {
122
123			size++;
124
125			__tt = realloc(__tt, sizeof(*__tt) * (size + 2));
126			if (!__tt)
127				return THERMAL_ERROR;
128
129			__tt[size - 1].id = nla_get_u32(attr);
130		}
131
132		if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_TYPE)
133			__tt[size - 1].type = nla_get_u32(attr);
134
135		if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_TEMP)
136			__tt[size - 1].temp = nla_get_u32(attr);
137
138		if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_HYST)
139			__tt[size - 1].hyst = nla_get_u32(attr);
140	}
141
142	if (__tt)
143		__tt[size].id = -1;
144
145	tz->trip = __tt;
146
147	return THERMAL_SUCCESS;
148}
149
150static int parse_tz_get_temp(struct genl_info *info, struct thermal_zone *tz)
151{
152	int id = -1;
153
154	if (info->attrs[THERMAL_GENL_ATTR_TZ_ID])
155		id = nla_get_u32(info->attrs[THERMAL_GENL_ATTR_TZ_ID]);
156
157	if (tz->id != id)
158		return THERMAL_ERROR;
159
160	if (info->attrs[THERMAL_GENL_ATTR_TZ_TEMP])
161		tz->temp = nla_get_u32(info->attrs[THERMAL_GENL_ATTR_TZ_TEMP]);
162
163	return THERMAL_SUCCESS;
164}
165
166static int parse_tz_get_gov(struct genl_info *info, struct thermal_zone *tz)
167{
168	int id = -1;
169
170	if (info->attrs[THERMAL_GENL_ATTR_TZ_ID])
171		id = nla_get_u32(info->attrs[THERMAL_GENL_ATTR_TZ_ID]);
172
173	if (tz->id != id)
174		return THERMAL_ERROR;
175
176	if (info->attrs[THERMAL_GENL_ATTR_TZ_GOV_NAME]) {
177		nla_strlcpy(tz->governor,
178			    info->attrs[THERMAL_GENL_ATTR_TZ_GOV_NAME],
179			    THERMAL_NAME_LENGTH);
180	}
181
182	return THERMAL_SUCCESS;
183}
184
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185static int handle_netlink(struct nl_cache_ops *unused,
186			  struct genl_cmd *cmd,
187			  struct genl_info *info, void *arg)
188{
189	int ret;
190
191	switch (cmd->c_id) {
192
193	case THERMAL_GENL_CMD_TZ_GET_ID:
194		ret = parse_tz_get(info, arg);
195		break;
196
197	case THERMAL_GENL_CMD_CDEV_GET:
198		ret = parse_cdev_get(info, arg);
199		break;
200
201	case THERMAL_GENL_CMD_TZ_GET_TEMP:
202		ret = parse_tz_get_temp(info, arg);
203		break;
204
205	case THERMAL_GENL_CMD_TZ_GET_TRIP:
206		ret = parse_tz_get_trip(info, arg);
207		break;
208
209	case THERMAL_GENL_CMD_TZ_GET_GOV:
210		ret = parse_tz_get_gov(info, arg);
211		break;
212
 
 
 
 
213	default:
214		return THERMAL_ERROR;
215	}
216
217	return ret;
218}
219
220static struct genl_cmd thermal_cmds[] = {
221	{
222		.c_id		= THERMAL_GENL_CMD_TZ_GET_ID,
223		.c_name		= (char *)"List thermal zones",
224		.c_msg_parser	= handle_netlink,
225		.c_maxattr	= THERMAL_GENL_ATTR_MAX,
226		.c_attr_policy	= thermal_genl_policy,
227	},
228	{
229		.c_id		= THERMAL_GENL_CMD_TZ_GET_GOV,
230		.c_name		= (char *)"Get governor",
231		.c_msg_parser	= handle_netlink,
232		.c_maxattr	= THERMAL_GENL_ATTR_MAX,
233		.c_attr_policy	= thermal_genl_policy,
234	},
235	{
236		.c_id		= THERMAL_GENL_CMD_TZ_GET_TEMP,
237		.c_name		= (char *)"Get thermal zone temperature",
238		.c_msg_parser	= handle_netlink,
239		.c_maxattr	= THERMAL_GENL_ATTR_MAX,
240		.c_attr_policy	= thermal_genl_policy,
241	},
242	{
243		.c_id		= THERMAL_GENL_CMD_TZ_GET_TRIP,
244		.c_name		= (char *)"Get thermal zone trip points",
245		.c_msg_parser	= handle_netlink,
246		.c_maxattr	= THERMAL_GENL_ATTR_MAX,
247		.c_attr_policy	= thermal_genl_policy,
248	},
249	{
250		.c_id		= THERMAL_GENL_CMD_CDEV_GET,
251		.c_name		= (char *)"Get cooling devices",
252		.c_msg_parser	= handle_netlink,
253		.c_maxattr	= THERMAL_GENL_ATTR_MAX,
254		.c_attr_policy	= thermal_genl_policy,
255	},
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256};
257
258static struct genl_ops thermal_cmd_ops = {
259	.o_name		= (char *)"thermal",
260	.o_cmds		= thermal_cmds,
261	.o_ncmds	= ARRAY_SIZE(thermal_cmds),
262};
263
264static thermal_error_t thermal_genl_auto(struct thermal_handler *th, int id, int cmd,
265					 int flags, void *arg)
 
 
 
 
 
 
 
266{
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
267	struct nl_msg *msg;
268	void *hdr;
269
270	msg = nlmsg_alloc();
271	if (!msg)
272		return THERMAL_ERROR;
273
274	hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, thermal_cmd_ops.o_id,
275			  0, flags, cmd, THERMAL_GENL_VERSION);
276	if (!hdr)
277		return THERMAL_ERROR;
278
279	if (id >= 0 && nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id))
280		return THERMAL_ERROR;
281
282	if (nl_send_msg(th->sk_cmd, th->cb_cmd, msg, genl_handle_msg, arg))
283		return THERMAL_ERROR;
284
 
 
285	nlmsg_free(msg);
286
287	return THERMAL_SUCCESS;
288}
289
290thermal_error_t thermal_cmd_get_tz(struct thermal_handler *th, struct thermal_zone **tz)
291{
292	return thermal_genl_auto(th, -1, THERMAL_GENL_CMD_TZ_GET_ID,
293				 NLM_F_DUMP | NLM_F_ACK, tz);
294}
295
296thermal_error_t thermal_cmd_get_cdev(struct thermal_handler *th, struct thermal_cdev **tc)
297{
298	return thermal_genl_auto(th, -1, THERMAL_GENL_CMD_CDEV_GET,
299				 NLM_F_DUMP | NLM_F_ACK, tc);
300}
301
302thermal_error_t thermal_cmd_get_trip(struct thermal_handler *th, struct thermal_zone *tz)
303{
304	return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_TRIP,
305				 0, tz);
 
 
306}
307
308thermal_error_t thermal_cmd_get_governor(struct thermal_handler *th, struct thermal_zone *tz)
309{
310	return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_GOV, 0, tz);
 
 
 
311}
312
313thermal_error_t thermal_cmd_get_temp(struct thermal_handler *th, struct thermal_zone *tz)
314{
315	return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_TEMP, 0, tz);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
316}
317
318thermal_error_t thermal_cmd_exit(struct thermal_handler *th)
319{
320	if (genl_unregister_family(&thermal_cmd_ops))
321		return THERMAL_ERROR;
322
323	nl_thermal_disconnect(th->sk_cmd, th->cb_cmd);
324
325	return THERMAL_SUCCESS;
326}
327
328thermal_error_t thermal_cmd_init(struct thermal_handler *th)
329{
330	int ret;
331	int family;
332
333	if (nl_thermal_connect(&th->sk_cmd, &th->cb_cmd))
334		return THERMAL_ERROR;
335
336	ret = genl_register_family(&thermal_cmd_ops);
337	if (ret)
338		return THERMAL_ERROR;
339
340	ret = genl_ops_resolve(th->sk_cmd, &thermal_cmd_ops);
341	if (ret)
342		return THERMAL_ERROR;
343
344	family = genl_ctrl_resolve(th->sk_cmd, "nlctrl");
345	if (family != GENL_ID_CTRL)
346		return THERMAL_ERROR;
347
348	return THERMAL_SUCCESS;
349}
v6.13.7
  1// SPDX-License-Identifier: LGPL-2.1+
  2// Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org>
  3#define _GNU_SOURCE
  4#include <errno.h>
  5#include <stdio.h>
  6#include <stdlib.h>
  7#include <unistd.h>
  8#include <limits.h>
  9
 10#include <thermal.h>
 11#include "thermal_nl.h"
 12
 13static struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = {
 14	/* Thermal zone */
 15	[THERMAL_GENL_ATTR_TZ]                  = { .type = NLA_NESTED },
 16	[THERMAL_GENL_ATTR_TZ_ID]               = { .type = NLA_U32 },
 17	[THERMAL_GENL_ATTR_TZ_TEMP]             = { .type = NLA_U32 },
 18	[THERMAL_GENL_ATTR_TZ_TRIP]             = { .type = NLA_NESTED },
 19	[THERMAL_GENL_ATTR_TZ_TRIP_ID]          = { .type = NLA_U32 },
 20	[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]        = { .type = NLA_U32 },
 21	[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]        = { .type = NLA_U32 },
 22	[THERMAL_GENL_ATTR_TZ_TRIP_HYST]        = { .type = NLA_U32 },
 23	[THERMAL_GENL_ATTR_TZ_MODE]             = { .type = NLA_U32 },
 24	[THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT]      = { .type = NLA_U32 },
 25	[THERMAL_GENL_ATTR_TZ_NAME]             = { .type = NLA_STRING },
 26
 27	/* Governor(s) */
 28	[THERMAL_GENL_ATTR_TZ_GOV]              = { .type = NLA_NESTED },
 29	[THERMAL_GENL_ATTR_TZ_GOV_NAME]         = { .type = NLA_STRING },
 30
 31	/* Cooling devices */
 32	[THERMAL_GENL_ATTR_CDEV]                = { .type = NLA_NESTED },
 33	[THERMAL_GENL_ATTR_CDEV_ID]             = { .type = NLA_U32 },
 34	[THERMAL_GENL_ATTR_CDEV_CUR_STATE]      = { .type = NLA_U32 },
 35	[THERMAL_GENL_ATTR_CDEV_MAX_STATE]      = { .type = NLA_U32 },
 36	[THERMAL_GENL_ATTR_CDEV_NAME]           = { .type = NLA_STRING },
 37
 38        /* Thresholds */
 39        [THERMAL_GENL_ATTR_THRESHOLD]      	= { .type = NLA_NESTED },
 40        [THERMAL_GENL_ATTR_THRESHOLD_TEMP]      = { .type = NLA_U32 },
 41        [THERMAL_GENL_ATTR_THRESHOLD_DIRECTION] = { .type = NLA_U32 },
 42};
 43
 44static int parse_tz_get(struct genl_info *info, struct thermal_zone **tz)
 45{
 46	struct nlattr *attr;
 47	struct thermal_zone *__tz = NULL;
 48	size_t size = 0;
 49	int rem;
 50
 51	nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_TZ], rem) {
 52
 53		if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_ID) {
 54
 55			size++;
 56
 57			__tz = realloc(__tz, sizeof(*__tz) * (size + 2));
 58			if (!__tz)
 59				return THERMAL_ERROR;
 60
 61			__tz[size - 1].id = nla_get_u32(attr);
 62		}
 63
 64
 65		if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_NAME)
 66			nla_strlcpy(__tz[size - 1].name, attr,
 67				    THERMAL_NAME_LENGTH);
 68	}
 69
 70	if (__tz)
 71		__tz[size].id = -1;
 72
 73	*tz = __tz;
 74
 75	return THERMAL_SUCCESS;
 76}
 77
 78static int parse_cdev_get(struct genl_info *info, struct thermal_cdev **cdev)
 79{
 80	struct nlattr *attr;
 81	struct thermal_cdev *__cdev = NULL;
 82	size_t size = 0;
 83	int rem;
 84
 85	nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_CDEV], rem) {
 86
 87		if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_ID) {
 88
 89			size++;
 90
 91			__cdev = realloc(__cdev, sizeof(*__cdev) * (size + 2));
 92			if (!__cdev)
 93				return THERMAL_ERROR;
 94
 95			__cdev[size - 1].id = nla_get_u32(attr);
 96		}
 97
 98		if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_NAME) {
 99			nla_strlcpy(__cdev[size - 1].name, attr,
100				    THERMAL_NAME_LENGTH);
101		}
102
103		if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_CUR_STATE)
104			__cdev[size - 1].cur_state = nla_get_u32(attr);
105
106		if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_MAX_STATE)
107			__cdev[size - 1].max_state = nla_get_u32(attr);
108	}
109
110	if (__cdev)
111		__cdev[size].id = -1;
112
113	*cdev = __cdev;
114
115	return THERMAL_SUCCESS;
116}
117
118static int parse_tz_get_trip(struct genl_info *info, struct thermal_zone *tz)
119{
120	struct nlattr *attr;
121	struct thermal_trip *__tt = NULL;
122	size_t size = 0;
123	int rem;
124
125	nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_TZ_TRIP], rem) {
126
127		if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_ID) {
128
129			size++;
130
131			__tt = realloc(__tt, sizeof(*__tt) * (size + 2));
132			if (!__tt)
133				return THERMAL_ERROR;
134
135			__tt[size - 1].id = nla_get_u32(attr);
136		}
137
138		if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_TYPE)
139			__tt[size - 1].type = nla_get_u32(attr);
140
141		if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_TEMP)
142			__tt[size - 1].temp = nla_get_u32(attr);
143
144		if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_HYST)
145			__tt[size - 1].hyst = nla_get_u32(attr);
146	}
147
148	if (__tt)
149		__tt[size].id = -1;
150
151	tz->trip = __tt;
152
153	return THERMAL_SUCCESS;
154}
155
156static int parse_tz_get_temp(struct genl_info *info, struct thermal_zone *tz)
157{
158	int id = -1;
159
160	if (info->attrs[THERMAL_GENL_ATTR_TZ_ID])
161		id = nla_get_u32(info->attrs[THERMAL_GENL_ATTR_TZ_ID]);
162
163	if (tz->id != id)
164		return THERMAL_ERROR;
165
166	if (info->attrs[THERMAL_GENL_ATTR_TZ_TEMP])
167		tz->temp = nla_get_u32(info->attrs[THERMAL_GENL_ATTR_TZ_TEMP]);
168
169	return THERMAL_SUCCESS;
170}
171
172static int parse_tz_get_gov(struct genl_info *info, struct thermal_zone *tz)
173{
174	int id = -1;
175
176	if (info->attrs[THERMAL_GENL_ATTR_TZ_ID])
177		id = nla_get_u32(info->attrs[THERMAL_GENL_ATTR_TZ_ID]);
178
179	if (tz->id != id)
180		return THERMAL_ERROR;
181
182	if (info->attrs[THERMAL_GENL_ATTR_TZ_GOV_NAME]) {
183		nla_strlcpy(tz->governor,
184			    info->attrs[THERMAL_GENL_ATTR_TZ_GOV_NAME],
185			    THERMAL_NAME_LENGTH);
186	}
187
188	return THERMAL_SUCCESS;
189}
190
191static int parse_threshold_get(struct genl_info *info, struct thermal_zone *tz)
192{
193	struct nlattr *attr;
194	struct thermal_threshold *__tt = NULL;
195	size_t size = 0;
196	int rem;
197
198	/*
199	 * The size contains the size of the array and we want to
200	 * access the last element, size - 1.
201	 *
202	 * The variable size is initialized to zero but it will be
203	 * then incremented by the first if() statement. The message
204	 * attributes are ordered, so the first if() statement will be
205	 * always called before the second one. If it happens that is
206	 * not the case, then it is a kernel bug.
207	 */
208	nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_THRESHOLD], rem) {
209
210		if (nla_type(attr) == THERMAL_GENL_ATTR_THRESHOLD_TEMP) {
211
212			size++;
213
214			__tt = realloc(__tt, sizeof(*__tt) * (size + 2));
215			if (!__tt)
216				return THERMAL_ERROR;
217
218			__tt[size - 1].temperature = nla_get_u32(attr);
219		}
220
221		if (nla_type(attr) == THERMAL_GENL_ATTR_THRESHOLD_DIRECTION)
222			__tt[size - 1].direction = nla_get_u32(attr);
223	}
224
225	if (__tt)
226		__tt[size].temperature = INT_MAX;
227
228	tz->thresholds = __tt;
229
230	return THERMAL_SUCCESS;
231}
232
233static int handle_netlink(struct nl_cache_ops *unused,
234			  struct genl_cmd *cmd,
235			  struct genl_info *info, void *arg)
236{
237	int ret;
238
239	switch (cmd->c_id) {
240
241	case THERMAL_GENL_CMD_TZ_GET_ID:
242		ret = parse_tz_get(info, arg);
243		break;
244
245	case THERMAL_GENL_CMD_CDEV_GET:
246		ret = parse_cdev_get(info, arg);
247		break;
248
249	case THERMAL_GENL_CMD_TZ_GET_TEMP:
250		ret = parse_tz_get_temp(info, arg);
251		break;
252
253	case THERMAL_GENL_CMD_TZ_GET_TRIP:
254		ret = parse_tz_get_trip(info, arg);
255		break;
256
257	case THERMAL_GENL_CMD_TZ_GET_GOV:
258		ret = parse_tz_get_gov(info, arg);
259		break;
260
261	case THERMAL_GENL_CMD_THRESHOLD_GET:
262		ret = parse_threshold_get(info, arg);
263		break;
264
265	default:
266		return THERMAL_ERROR;
267	}
268
269	return ret;
270}
271
272static struct genl_cmd thermal_cmds[] = {
273	{
274		.c_id		= THERMAL_GENL_CMD_TZ_GET_ID,
275		.c_name		= (char *)"List thermal zones",
276		.c_msg_parser	= handle_netlink,
277		.c_maxattr	= THERMAL_GENL_ATTR_MAX,
278		.c_attr_policy	= thermal_genl_policy,
279	},
280	{
281		.c_id		= THERMAL_GENL_CMD_TZ_GET_GOV,
282		.c_name		= (char *)"Get governor",
283		.c_msg_parser	= handle_netlink,
284		.c_maxattr	= THERMAL_GENL_ATTR_MAX,
285		.c_attr_policy	= thermal_genl_policy,
286	},
287	{
288		.c_id		= THERMAL_GENL_CMD_TZ_GET_TEMP,
289		.c_name		= (char *)"Get thermal zone temperature",
290		.c_msg_parser	= handle_netlink,
291		.c_maxattr	= THERMAL_GENL_ATTR_MAX,
292		.c_attr_policy	= thermal_genl_policy,
293	},
294	{
295		.c_id		= THERMAL_GENL_CMD_TZ_GET_TRIP,
296		.c_name		= (char *)"Get thermal zone trip points",
297		.c_msg_parser	= handle_netlink,
298		.c_maxattr	= THERMAL_GENL_ATTR_MAX,
299		.c_attr_policy	= thermal_genl_policy,
300	},
301	{
302		.c_id		= THERMAL_GENL_CMD_CDEV_GET,
303		.c_name		= (char *)"Get cooling devices",
304		.c_msg_parser	= handle_netlink,
305		.c_maxattr	= THERMAL_GENL_ATTR_MAX,
306		.c_attr_policy	= thermal_genl_policy,
307	},
308        {
309                .c_id           = THERMAL_GENL_CMD_THRESHOLD_GET,
310                .c_name         = (char *)"Get thresholds list",
311                .c_msg_parser   = handle_netlink,
312                .c_maxattr      = THERMAL_GENL_ATTR_MAX,
313                .c_attr_policy  = thermal_genl_policy,
314        },
315        {
316                .c_id           = THERMAL_GENL_CMD_THRESHOLD_ADD,
317                .c_name         = (char *)"Add a threshold",
318                .c_msg_parser   = handle_netlink,
319                .c_maxattr      = THERMAL_GENL_ATTR_MAX,
320                .c_attr_policy  = thermal_genl_policy,
321        },
322        {
323                .c_id           = THERMAL_GENL_CMD_THRESHOLD_DELETE,
324                .c_name         = (char *)"Delete a threshold",
325                .c_msg_parser   = handle_netlink,
326                .c_maxattr      = THERMAL_GENL_ATTR_MAX,
327                .c_attr_policy  = thermal_genl_policy,
328        },
329        {
330                .c_id           = THERMAL_GENL_CMD_THRESHOLD_FLUSH,
331                .c_name         = (char *)"Flush the thresholds",
332                .c_msg_parser   = handle_netlink,
333                .c_maxattr      = THERMAL_GENL_ATTR_MAX,
334                .c_attr_policy  = thermal_genl_policy,
335        },
336};
337
338static struct genl_ops thermal_cmd_ops = {
339	.o_name		= (char *)"thermal",
340	.o_cmds		= thermal_cmds,
341	.o_ncmds	= ARRAY_SIZE(thermal_cmds),
342};
343
344struct cmd_param {
345	int tz_id;
346	int temp;
347	int direction;
348};
349
350typedef int (*cmd_cb_t)(struct nl_msg *, struct cmd_param *);
351
352static int thermal_genl_tz_id_encode(struct nl_msg *msg, struct cmd_param *p)
353{
354	if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id))
355		return -1;
356
357	return 0;
358}
359
360static int thermal_genl_threshold_encode(struct nl_msg *msg, struct cmd_param *p)
361{
362	if (thermal_genl_tz_id_encode(msg, p))
363		return -1;
364
365	if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, p->temp))
366		return -1;
367
368	if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_DIRECTION, p->direction))
369		return -1;
370
371	return 0;
372}
373
374static thermal_error_t thermal_genl_auto(struct thermal_handler *th, cmd_cb_t cmd_cb,
375					 struct cmd_param *param,
376					 int cmd, int flags, void *arg)
377{
378	thermal_error_t ret = THERMAL_ERROR;
379	struct nl_msg *msg;
380	void *hdr;
381
382	msg = nlmsg_alloc();
383	if (!msg)
384		return THERMAL_ERROR;
385
386	hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, thermal_cmd_ops.o_id,
387			  0, flags, cmd, THERMAL_GENL_VERSION);
388	if (!hdr)
389		goto out;
390
391	if (cmd_cb && cmd_cb(msg, param))
392		goto out;
393
394	if (nl_send_msg(th->sk_cmd, th->cb_cmd, msg, genl_handle_msg, arg))
395		goto out;
396
397	ret = THERMAL_SUCCESS;
398out:
399	nlmsg_free(msg);
400
401	return ret;
402}
403
404thermal_error_t thermal_cmd_get_tz(struct thermal_handler *th, struct thermal_zone **tz)
405{
406	return thermal_genl_auto(th, NULL, NULL, THERMAL_GENL_CMD_TZ_GET_ID,
407				 NLM_F_DUMP | NLM_F_ACK, tz);
408}
409
410thermal_error_t thermal_cmd_get_cdev(struct thermal_handler *th, struct thermal_cdev **tc)
411{
412	return thermal_genl_auto(th, NULL, NULL, THERMAL_GENL_CMD_CDEV_GET,
413				 NLM_F_DUMP | NLM_F_ACK, tc);
414}
415
416thermal_error_t thermal_cmd_get_trip(struct thermal_handler *th, struct thermal_zone *tz)
417{
418	struct cmd_param p = { .tz_id = tz->id };
419
420	return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p,
421				 THERMAL_GENL_CMD_TZ_GET_TRIP, 0, tz);
422}
423
424thermal_error_t thermal_cmd_get_governor(struct thermal_handler *th, struct thermal_zone *tz)
425{
426	struct cmd_param p = { .tz_id = tz->id };
427
428	return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p,
429				 THERMAL_GENL_CMD_TZ_GET_GOV, 0, tz);
430}
431
432thermal_error_t thermal_cmd_get_temp(struct thermal_handler *th, struct thermal_zone *tz)
433{
434	struct cmd_param p = { .tz_id = tz->id };
435
436	return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p,
437				 THERMAL_GENL_CMD_TZ_GET_TEMP, 0, tz);
438}
439
440thermal_error_t thermal_cmd_threshold_get(struct thermal_handler *th,
441                                          struct thermal_zone *tz)
442{
443	struct cmd_param p = { .tz_id = tz->id };
444
445        return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p,
446				 THERMAL_GENL_CMD_THRESHOLD_GET, 0, tz);
447}
448
449thermal_error_t thermal_cmd_threshold_add(struct thermal_handler *th,
450                                          struct thermal_zone *tz,
451                                          int temperature,
452                                          int direction)
453{
454	struct cmd_param p = { .tz_id = tz->id, .temp = temperature, .direction = direction };
455
456        return thermal_genl_auto(th, thermal_genl_threshold_encode, &p,
457				 THERMAL_GENL_CMD_THRESHOLD_ADD, 0, tz);
458}
459
460thermal_error_t thermal_cmd_threshold_delete(struct thermal_handler *th,
461                                             struct thermal_zone *tz,
462                                             int temperature,
463                                             int direction)
464{
465	struct cmd_param p = { .tz_id = tz->id, .temp = temperature, .direction = direction };
466
467        return thermal_genl_auto(th, thermal_genl_threshold_encode, &p,
468				 THERMAL_GENL_CMD_THRESHOLD_DELETE, 0, tz);
469}
470
471thermal_error_t thermal_cmd_threshold_flush(struct thermal_handler *th,
472                                            struct thermal_zone *tz)
473{
474	struct cmd_param p = { .tz_id = tz->id };
475
476        return thermal_genl_auto(th, thermal_genl_tz_id_encode, &p,
477				 THERMAL_GENL_CMD_THRESHOLD_FLUSH, 0, tz);
478}
479
480thermal_error_t thermal_cmd_exit(struct thermal_handler *th)
481{
482	if (genl_unregister_family(&thermal_cmd_ops))
483		return THERMAL_ERROR;
484
485	nl_thermal_disconnect(th->sk_cmd, th->cb_cmd);
486
487	return THERMAL_SUCCESS;
488}
489
490thermal_error_t thermal_cmd_init(struct thermal_handler *th)
491{
492	int ret;
493	int family;
494
495	if (nl_thermal_connect(&th->sk_cmd, &th->cb_cmd))
496		return THERMAL_ERROR;
497
498	ret = genl_register_family(&thermal_cmd_ops);
499	if (ret)
500		return THERMAL_ERROR;
501
502	ret = genl_ops_resolve(th->sk_cmd, &thermal_cmd_ops);
503	if (ret)
504		return THERMAL_ERROR;
505
506	family = genl_ctrl_resolve(th->sk_cmd, "nlctrl");
507	if (family != GENL_ID_CTRL)
508		return THERMAL_ERROR;
509
510	return THERMAL_SUCCESS;
511}