xref: /openbmc/linux/drivers/thermal/thermal_netlink.c (revision e5bfcd30f88fdb0ce830229e7ccdeddcb7a59b04)
11ce50e7dSDaniel Lezcano // SPDX-License-Identifier: GPL-2.0
21ce50e7dSDaniel Lezcano /*
31ce50e7dSDaniel Lezcano  * Copyright 2020 Linaro Limited
41ce50e7dSDaniel Lezcano  *
51ce50e7dSDaniel Lezcano  * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
61ce50e7dSDaniel Lezcano  *
71ce50e7dSDaniel Lezcano  * Generic netlink for thermal management framework
81ce50e7dSDaniel Lezcano  */
91ce50e7dSDaniel Lezcano #include <linux/module.h>
101ce50e7dSDaniel Lezcano #include <linux/kernel.h>
111ce50e7dSDaniel Lezcano #include <net/genetlink.h>
121ce50e7dSDaniel Lezcano #include <uapi/linux/thermal.h>
131ce50e7dSDaniel Lezcano 
141ce50e7dSDaniel Lezcano #include "thermal_core.h"
151ce50e7dSDaniel Lezcano 
161ce50e7dSDaniel Lezcano static const struct genl_multicast_group thermal_genl_mcgrps[] = {
171ce50e7dSDaniel Lezcano 	{ .name = THERMAL_GENL_SAMPLING_GROUP_NAME, },
181ce50e7dSDaniel Lezcano 	{ .name = THERMAL_GENL_EVENT_GROUP_NAME,  },
191ce50e7dSDaniel Lezcano };
201ce50e7dSDaniel Lezcano 
211ce50e7dSDaniel Lezcano static const struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = {
221ce50e7dSDaniel Lezcano 	/* Thermal zone */
231ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ]			= { .type = NLA_NESTED },
241ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_ID]		= { .type = NLA_U32 },
251ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_TEMP]		= { .type = NLA_U32 },
261ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_TRIP]		= { .type = NLA_NESTED },
271ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_TRIP_ID]		= { .type = NLA_U32 },
281ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]	= { .type = NLA_U32 },
291ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]	= { .type = NLA_U32 },
301ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_TRIP_HYST]	= { .type = NLA_U32 },
311ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_MODE]		= { .type = NLA_U32 },
321ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT]	= { .type = NLA_U32 },
331ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_NAME]		= { .type = NLA_STRING,
341ce50e7dSDaniel Lezcano 						    .len = THERMAL_NAME_LENGTH },
351ce50e7dSDaniel Lezcano 	/* Governor(s) */
361ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_GOV]		= { .type = NLA_NESTED },
371ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_GOV_NAME]		= { .type = NLA_STRING,
381ce50e7dSDaniel Lezcano 						    .len = THERMAL_NAME_LENGTH },
391ce50e7dSDaniel Lezcano 	/* Cooling devices */
401ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_CDEV]		= { .type = NLA_NESTED },
411ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_CDEV_ID]		= { .type = NLA_U32 },
421ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_CDEV_CUR_STATE]	= { .type = NLA_U32 },
431ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_CDEV_MAX_STATE]	= { .type = NLA_U32 },
441ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_CDEV_NAME]		= { .type = NLA_STRING,
451ce50e7dSDaniel Lezcano 						    .len = THERMAL_NAME_LENGTH },
46e4b1eb24SSrinivas Pandruvada 	/* CPU capabilities */
47e4b1eb24SSrinivas Pandruvada 	[THERMAL_GENL_ATTR_CPU_CAPABILITY]		= { .type = NLA_NESTED },
48e4b1eb24SSrinivas Pandruvada 	[THERMAL_GENL_ATTR_CPU_CAPABILITY_ID]		= { .type = NLA_U32 },
49e4b1eb24SSrinivas Pandruvada 	[THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE]	= { .type = NLA_U32 },
50e4b1eb24SSrinivas Pandruvada 	[THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY]	= { .type = NLA_U32 },
511ce50e7dSDaniel Lezcano };
521ce50e7dSDaniel Lezcano 
531ce50e7dSDaniel Lezcano struct param {
541ce50e7dSDaniel Lezcano 	struct nlattr **attrs;
551ce50e7dSDaniel Lezcano 	struct sk_buff *msg;
561ce50e7dSDaniel Lezcano 	const char *name;
571ce50e7dSDaniel Lezcano 	int tz_id;
581ce50e7dSDaniel Lezcano 	int cdev_id;
591ce50e7dSDaniel Lezcano 	int trip_id;
601ce50e7dSDaniel Lezcano 	int trip_temp;
611ce50e7dSDaniel Lezcano 	int trip_type;
621ce50e7dSDaniel Lezcano 	int trip_hyst;
631ce50e7dSDaniel Lezcano 	int temp;
641ce50e7dSDaniel Lezcano 	int cdev_state;
651ce50e7dSDaniel Lezcano 	int cdev_max_state;
66e4b1eb24SSrinivas Pandruvada 	struct thermal_genl_cpu_caps *cpu_capabilities;
67e4b1eb24SSrinivas Pandruvada 	int cpu_capabilities_count;
681ce50e7dSDaniel Lezcano };
691ce50e7dSDaniel Lezcano 
701ce50e7dSDaniel Lezcano typedef int (*cb_t)(struct param *);
711ce50e7dSDaniel Lezcano 
721ce50e7dSDaniel Lezcano static struct genl_family thermal_gnl_family;
731ce50e7dSDaniel Lezcano 
741ce50e7dSDaniel Lezcano /************************** Sampling encoding *******************************/
751ce50e7dSDaniel Lezcano 
761ce50e7dSDaniel Lezcano int thermal_genl_sampling_temp(int id, int temp)
771ce50e7dSDaniel Lezcano {
781ce50e7dSDaniel Lezcano 	struct sk_buff *skb;
791ce50e7dSDaniel Lezcano 	void *hdr;
801ce50e7dSDaniel Lezcano 
811ce50e7dSDaniel Lezcano 	skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
821ce50e7dSDaniel Lezcano 	if (!skb)
831ce50e7dSDaniel Lezcano 		return -ENOMEM;
841ce50e7dSDaniel Lezcano 
851ce50e7dSDaniel Lezcano 	hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0,
861ce50e7dSDaniel Lezcano 			  THERMAL_GENL_SAMPLING_TEMP);
871ce50e7dSDaniel Lezcano 	if (!hdr)
8848b45859SJing Xiangfeng 		goto out_free;
891ce50e7dSDaniel Lezcano 
901ce50e7dSDaniel Lezcano 	if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_ID, id))
911ce50e7dSDaniel Lezcano 		goto out_cancel;
921ce50e7dSDaniel Lezcano 
931ce50e7dSDaniel Lezcano 	if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_TEMP, temp))
941ce50e7dSDaniel Lezcano 		goto out_cancel;
951ce50e7dSDaniel Lezcano 
961ce50e7dSDaniel Lezcano 	genlmsg_end(skb, hdr);
971ce50e7dSDaniel Lezcano 
981ce50e7dSDaniel Lezcano 	genlmsg_multicast(&thermal_gnl_family, skb, 0, 0, GFP_KERNEL);
991ce50e7dSDaniel Lezcano 
1001ce50e7dSDaniel Lezcano 	return 0;
1011ce50e7dSDaniel Lezcano out_cancel:
1021ce50e7dSDaniel Lezcano 	genlmsg_cancel(skb, hdr);
10348b45859SJing Xiangfeng out_free:
1041ce50e7dSDaniel Lezcano 	nlmsg_free(skb);
1051ce50e7dSDaniel Lezcano 
1061ce50e7dSDaniel Lezcano 	return -EMSGSIZE;
1071ce50e7dSDaniel Lezcano }
1081ce50e7dSDaniel Lezcano 
1091ce50e7dSDaniel Lezcano /**************************** Event encoding *********************************/
1101ce50e7dSDaniel Lezcano 
1111ce50e7dSDaniel Lezcano static int thermal_genl_event_tz_create(struct param *p)
1121ce50e7dSDaniel Lezcano {
1131ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
1141ce50e7dSDaniel Lezcano 	    nla_put_string(p->msg, THERMAL_GENL_ATTR_TZ_NAME, p->name))
1151ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
1161ce50e7dSDaniel Lezcano 
1171ce50e7dSDaniel Lezcano 	return 0;
1181ce50e7dSDaniel Lezcano }
1191ce50e7dSDaniel Lezcano 
1201ce50e7dSDaniel Lezcano static int thermal_genl_event_tz(struct param *p)
1211ce50e7dSDaniel Lezcano {
1221ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id))
1231ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
1241ce50e7dSDaniel Lezcano 
1251ce50e7dSDaniel Lezcano 	return 0;
1261ce50e7dSDaniel Lezcano }
1271ce50e7dSDaniel Lezcano 
1281ce50e7dSDaniel Lezcano static int thermal_genl_event_tz_trip_up(struct param *p)
1291ce50e7dSDaniel Lezcano {
1301ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
131fc656fa1SDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id) ||
132fc656fa1SDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TEMP, p->temp))
1331ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
1341ce50e7dSDaniel Lezcano 
1351ce50e7dSDaniel Lezcano 	return 0;
1361ce50e7dSDaniel Lezcano }
1371ce50e7dSDaniel Lezcano 
1381ce50e7dSDaniel Lezcano static int thermal_genl_event_tz_trip_add(struct param *p)
1391ce50e7dSDaniel Lezcano {
1401ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
1411ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id) ||
1421ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, p->trip_type) ||
1431ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, p->trip_temp) ||
1441ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, p->trip_hyst))
1451ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
1461ce50e7dSDaniel Lezcano 
1471ce50e7dSDaniel Lezcano 	return 0;
1481ce50e7dSDaniel Lezcano }
1491ce50e7dSDaniel Lezcano 
1501ce50e7dSDaniel Lezcano static int thermal_genl_event_tz_trip_delete(struct param *p)
1511ce50e7dSDaniel Lezcano {
1521ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
1531ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id))
1541ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
1551ce50e7dSDaniel Lezcano 
1561ce50e7dSDaniel Lezcano 	return 0;
1571ce50e7dSDaniel Lezcano }
1581ce50e7dSDaniel Lezcano 
1591ce50e7dSDaniel Lezcano static int thermal_genl_event_cdev_add(struct param *p)
1601ce50e7dSDaniel Lezcano {
1611ce50e7dSDaniel Lezcano 	if (nla_put_string(p->msg, THERMAL_GENL_ATTR_CDEV_NAME,
1621ce50e7dSDaniel Lezcano 			   p->name) ||
1631ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID,
1641ce50e7dSDaniel Lezcano 			p->cdev_id) ||
1651ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_MAX_STATE,
1661ce50e7dSDaniel Lezcano 			p->cdev_max_state))
1671ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
1681ce50e7dSDaniel Lezcano 
1691ce50e7dSDaniel Lezcano 	return 0;
1701ce50e7dSDaniel Lezcano }
1711ce50e7dSDaniel Lezcano 
1721ce50e7dSDaniel Lezcano static int thermal_genl_event_cdev_delete(struct param *p)
1731ce50e7dSDaniel Lezcano {
1741ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID, p->cdev_id))
1751ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
1761ce50e7dSDaniel Lezcano 
1771ce50e7dSDaniel Lezcano 	return 0;
1781ce50e7dSDaniel Lezcano }
1791ce50e7dSDaniel Lezcano 
1801ce50e7dSDaniel Lezcano static int thermal_genl_event_cdev_state_update(struct param *p)
1811ce50e7dSDaniel Lezcano {
1821ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID,
1831ce50e7dSDaniel Lezcano 			p->cdev_id) ||
1841ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_CUR_STATE,
1851ce50e7dSDaniel Lezcano 			p->cdev_state))
1861ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
1871ce50e7dSDaniel Lezcano 
1881ce50e7dSDaniel Lezcano 	return 0;
1891ce50e7dSDaniel Lezcano }
1901ce50e7dSDaniel Lezcano 
1911ce50e7dSDaniel Lezcano static int thermal_genl_event_gov_change(struct param *p)
1921ce50e7dSDaniel Lezcano {
1931ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
1941ce50e7dSDaniel Lezcano 	    nla_put_string(p->msg, THERMAL_GENL_ATTR_GOV_NAME, p->name))
1951ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
1961ce50e7dSDaniel Lezcano 
1971ce50e7dSDaniel Lezcano 	return 0;
1981ce50e7dSDaniel Lezcano }
1991ce50e7dSDaniel Lezcano 
200e4b1eb24SSrinivas Pandruvada static int thermal_genl_event_cpu_capability_change(struct param *p)
201e4b1eb24SSrinivas Pandruvada {
202e4b1eb24SSrinivas Pandruvada 	struct thermal_genl_cpu_caps *cpu_cap = p->cpu_capabilities;
203e4b1eb24SSrinivas Pandruvada 	struct sk_buff *msg = p->msg;
204e4b1eb24SSrinivas Pandruvada 	struct nlattr *start_cap;
205e4b1eb24SSrinivas Pandruvada 	int i;
206e4b1eb24SSrinivas Pandruvada 
207e4b1eb24SSrinivas Pandruvada 	start_cap = nla_nest_start(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY);
208e4b1eb24SSrinivas Pandruvada 	if (!start_cap)
209e4b1eb24SSrinivas Pandruvada 		return -EMSGSIZE;
210e4b1eb24SSrinivas Pandruvada 
211e4b1eb24SSrinivas Pandruvada 	for (i = 0; i < p->cpu_capabilities_count; ++i) {
212e4b1eb24SSrinivas Pandruvada 		if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_ID,
213e4b1eb24SSrinivas Pandruvada 				cpu_cap->cpu))
214e4b1eb24SSrinivas Pandruvada 			goto out_cancel_nest;
215e4b1eb24SSrinivas Pandruvada 
216e4b1eb24SSrinivas Pandruvada 		if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE,
217e4b1eb24SSrinivas Pandruvada 				cpu_cap->performance))
218e4b1eb24SSrinivas Pandruvada 			goto out_cancel_nest;
219e4b1eb24SSrinivas Pandruvada 
220e4b1eb24SSrinivas Pandruvada 		if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY,
221e4b1eb24SSrinivas Pandruvada 				cpu_cap->efficiency))
222e4b1eb24SSrinivas Pandruvada 			goto out_cancel_nest;
223e4b1eb24SSrinivas Pandruvada 
224e4b1eb24SSrinivas Pandruvada 		++cpu_cap;
225e4b1eb24SSrinivas Pandruvada 	}
226e4b1eb24SSrinivas Pandruvada 
227e4b1eb24SSrinivas Pandruvada 	nla_nest_end(msg, start_cap);
228e4b1eb24SSrinivas Pandruvada 
229e4b1eb24SSrinivas Pandruvada 	return 0;
230e4b1eb24SSrinivas Pandruvada out_cancel_nest:
231e4b1eb24SSrinivas Pandruvada 	nla_nest_cancel(msg, start_cap);
232e4b1eb24SSrinivas Pandruvada 
233e4b1eb24SSrinivas Pandruvada 	return -EMSGSIZE;
234e4b1eb24SSrinivas Pandruvada }
235e4b1eb24SSrinivas Pandruvada 
2361ce50e7dSDaniel Lezcano int thermal_genl_event_tz_delete(struct param *p)
2371ce50e7dSDaniel Lezcano 	__attribute__((alias("thermal_genl_event_tz")));
2381ce50e7dSDaniel Lezcano 
2391ce50e7dSDaniel Lezcano int thermal_genl_event_tz_enable(struct param *p)
2401ce50e7dSDaniel Lezcano 	__attribute__((alias("thermal_genl_event_tz")));
2411ce50e7dSDaniel Lezcano 
2421ce50e7dSDaniel Lezcano int thermal_genl_event_tz_disable(struct param *p)
2431ce50e7dSDaniel Lezcano 	__attribute__((alias("thermal_genl_event_tz")));
2441ce50e7dSDaniel Lezcano 
2451ce50e7dSDaniel Lezcano int thermal_genl_event_tz_trip_down(struct param *p)
2461ce50e7dSDaniel Lezcano 	__attribute__((alias("thermal_genl_event_tz_trip_up")));
2471ce50e7dSDaniel Lezcano 
2481ce50e7dSDaniel Lezcano int thermal_genl_event_tz_trip_change(struct param *p)
2491ce50e7dSDaniel Lezcano 	__attribute__((alias("thermal_genl_event_tz_trip_add")));
2501ce50e7dSDaniel Lezcano 
2511ce50e7dSDaniel Lezcano static cb_t event_cb[] = {
2521ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_CREATE]		= thermal_genl_event_tz_create,
2531ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_DELETE]		= thermal_genl_event_tz_delete,
2541ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_ENABLE]		= thermal_genl_event_tz_enable,
2551ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_DISABLE]		= thermal_genl_event_tz_disable,
2561ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_TRIP_UP]		= thermal_genl_event_tz_trip_up,
2571ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_TRIP_DOWN]	= thermal_genl_event_tz_trip_down,
2581ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_TRIP_CHANGE]	= thermal_genl_event_tz_trip_change,
2591ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_TRIP_ADD]	= thermal_genl_event_tz_trip_add,
2601ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_TRIP_DELETE]	= thermal_genl_event_tz_trip_delete,
2611ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_CDEV_ADD]		= thermal_genl_event_cdev_add,
2621ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_CDEV_DELETE]	= thermal_genl_event_cdev_delete,
2631ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE]	= thermal_genl_event_cdev_state_update,
2641ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_GOV_CHANGE]	= thermal_genl_event_gov_change,
265e4b1eb24SSrinivas Pandruvada 	[THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE] = thermal_genl_event_cpu_capability_change,
2661ce50e7dSDaniel Lezcano };
2671ce50e7dSDaniel Lezcano 
2681ce50e7dSDaniel Lezcano /*
2691ce50e7dSDaniel Lezcano  * Generic netlink event encoding
2701ce50e7dSDaniel Lezcano  */
2711ce50e7dSDaniel Lezcano static int thermal_genl_send_event(enum thermal_genl_event event,
2721ce50e7dSDaniel Lezcano 				   struct param *p)
2731ce50e7dSDaniel Lezcano {
2741ce50e7dSDaniel Lezcano 	struct sk_buff *msg;
2751ce50e7dSDaniel Lezcano 	int ret = -EMSGSIZE;
2761ce50e7dSDaniel Lezcano 	void *hdr;
2771ce50e7dSDaniel Lezcano 
2781ce50e7dSDaniel Lezcano 	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
2791ce50e7dSDaniel Lezcano 	if (!msg)
2801ce50e7dSDaniel Lezcano 		return -ENOMEM;
2811ce50e7dSDaniel Lezcano 	p->msg = msg;
2821ce50e7dSDaniel Lezcano 
2831ce50e7dSDaniel Lezcano 	hdr = genlmsg_put(msg, 0, 0, &thermal_gnl_family, 0, event);
2841ce50e7dSDaniel Lezcano 	if (!hdr)
2851ce50e7dSDaniel Lezcano 		goto out_free_msg;
2861ce50e7dSDaniel Lezcano 
2871ce50e7dSDaniel Lezcano 	ret = event_cb[event](p);
2881ce50e7dSDaniel Lezcano 	if (ret)
2891ce50e7dSDaniel Lezcano 		goto out_cancel_msg;
2901ce50e7dSDaniel Lezcano 
2911ce50e7dSDaniel Lezcano 	genlmsg_end(msg, hdr);
2921ce50e7dSDaniel Lezcano 
2931ce50e7dSDaniel Lezcano 	genlmsg_multicast(&thermal_gnl_family, msg, 0, 1, GFP_KERNEL);
2941ce50e7dSDaniel Lezcano 
2951ce50e7dSDaniel Lezcano 	return 0;
2961ce50e7dSDaniel Lezcano 
2971ce50e7dSDaniel Lezcano out_cancel_msg:
2981ce50e7dSDaniel Lezcano 	genlmsg_cancel(msg, hdr);
2991ce50e7dSDaniel Lezcano out_free_msg:
3001ce50e7dSDaniel Lezcano 	nlmsg_free(msg);
3011ce50e7dSDaniel Lezcano 
3021ce50e7dSDaniel Lezcano 	return ret;
3031ce50e7dSDaniel Lezcano }
3041ce50e7dSDaniel Lezcano 
3051ce50e7dSDaniel Lezcano int thermal_notify_tz_create(int tz_id, const char *name)
3061ce50e7dSDaniel Lezcano {
3071ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id, .name = name };
3081ce50e7dSDaniel Lezcano 
3091ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_CREATE, &p);
3101ce50e7dSDaniel Lezcano }
3111ce50e7dSDaniel Lezcano 
3121ce50e7dSDaniel Lezcano int thermal_notify_tz_delete(int tz_id)
3131ce50e7dSDaniel Lezcano {
3141ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id };
3151ce50e7dSDaniel Lezcano 
3161ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DELETE, &p);
3171ce50e7dSDaniel Lezcano }
3181ce50e7dSDaniel Lezcano 
3191ce50e7dSDaniel Lezcano int thermal_notify_tz_enable(int tz_id)
3201ce50e7dSDaniel Lezcano {
3211ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id };
3221ce50e7dSDaniel Lezcano 
3231ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_ENABLE, &p);
3241ce50e7dSDaniel Lezcano }
3251ce50e7dSDaniel Lezcano 
3261ce50e7dSDaniel Lezcano int thermal_notify_tz_disable(int tz_id)
3271ce50e7dSDaniel Lezcano {
3281ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id };
3291ce50e7dSDaniel Lezcano 
3301ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DISABLE, &p);
3311ce50e7dSDaniel Lezcano }
3321ce50e7dSDaniel Lezcano 
333fc656fa1SDaniel Lezcano int thermal_notify_tz_trip_down(int tz_id, int trip_id, int temp)
3341ce50e7dSDaniel Lezcano {
335fc656fa1SDaniel Lezcano 	struct param p = { .tz_id = tz_id, .trip_id = trip_id, .temp = temp };
3361ce50e7dSDaniel Lezcano 
3371ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DOWN, &p);
3381ce50e7dSDaniel Lezcano }
3391ce50e7dSDaniel Lezcano 
340fc656fa1SDaniel Lezcano int thermal_notify_tz_trip_up(int tz_id, int trip_id, int temp)
3411ce50e7dSDaniel Lezcano {
342fc656fa1SDaniel Lezcano 	struct param p = { .tz_id = tz_id, .trip_id = trip_id, .temp = temp };
3431ce50e7dSDaniel Lezcano 
3441ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_UP, &p);
3451ce50e7dSDaniel Lezcano }
3461ce50e7dSDaniel Lezcano 
3471ce50e7dSDaniel Lezcano int thermal_notify_tz_trip_add(int tz_id, int trip_id, int trip_type,
3481ce50e7dSDaniel Lezcano 			       int trip_temp, int trip_hyst)
3491ce50e7dSDaniel Lezcano {
3501ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id, .trip_id = trip_id,
3511ce50e7dSDaniel Lezcano 			   .trip_type = trip_type, .trip_temp = trip_temp,
3521ce50e7dSDaniel Lezcano 			   .trip_hyst = trip_hyst };
3531ce50e7dSDaniel Lezcano 
3541ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_ADD, &p);
3551ce50e7dSDaniel Lezcano }
3561ce50e7dSDaniel Lezcano 
3571ce50e7dSDaniel Lezcano int thermal_notify_tz_trip_delete(int tz_id, int trip_id)
3581ce50e7dSDaniel Lezcano {
3591ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id, .trip_id = trip_id };
3601ce50e7dSDaniel Lezcano 
3611ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DELETE, &p);
3621ce50e7dSDaniel Lezcano }
3631ce50e7dSDaniel Lezcano 
3641ce50e7dSDaniel Lezcano int thermal_notify_tz_trip_change(int tz_id, int trip_id, int trip_type,
3651ce50e7dSDaniel Lezcano 				  int trip_temp, int trip_hyst)
3661ce50e7dSDaniel Lezcano {
3671ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id, .trip_id = trip_id,
3681ce50e7dSDaniel Lezcano 			   .trip_type = trip_type, .trip_temp = trip_temp,
3691ce50e7dSDaniel Lezcano 			   .trip_hyst = trip_hyst };
3701ce50e7dSDaniel Lezcano 
3711ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_CHANGE, &p);
3721ce50e7dSDaniel Lezcano }
3731ce50e7dSDaniel Lezcano 
3741ce50e7dSDaniel Lezcano int thermal_notify_cdev_state_update(int cdev_id, int cdev_state)
3751ce50e7dSDaniel Lezcano {
3761ce50e7dSDaniel Lezcano 	struct param p = { .cdev_id = cdev_id, .cdev_state = cdev_state };
3771ce50e7dSDaniel Lezcano 
3781ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, &p);
3791ce50e7dSDaniel Lezcano }
3801ce50e7dSDaniel Lezcano 
3811ce50e7dSDaniel Lezcano int thermal_notify_cdev_add(int cdev_id, const char *name, int cdev_max_state)
3821ce50e7dSDaniel Lezcano {
3831ce50e7dSDaniel Lezcano 	struct param p = { .cdev_id = cdev_id, .name = name,
3841ce50e7dSDaniel Lezcano 			   .cdev_max_state = cdev_max_state };
3851ce50e7dSDaniel Lezcano 
3861ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_ADD, &p);
3871ce50e7dSDaniel Lezcano }
3881ce50e7dSDaniel Lezcano 
3891ce50e7dSDaniel Lezcano int thermal_notify_cdev_delete(int cdev_id)
3901ce50e7dSDaniel Lezcano {
3911ce50e7dSDaniel Lezcano 	struct param p = { .cdev_id = cdev_id };
3921ce50e7dSDaniel Lezcano 
3931ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_DELETE, &p);
3941ce50e7dSDaniel Lezcano }
3951ce50e7dSDaniel Lezcano 
3961ce50e7dSDaniel Lezcano int thermal_notify_tz_gov_change(int tz_id, const char *name)
3971ce50e7dSDaniel Lezcano {
3981ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id, .name = name };
3991ce50e7dSDaniel Lezcano 
4001ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_GOV_CHANGE, &p);
4011ce50e7dSDaniel Lezcano }
4021ce50e7dSDaniel Lezcano 
403e4b1eb24SSrinivas Pandruvada int thermal_genl_cpu_capability_event(int count,
404e4b1eb24SSrinivas Pandruvada 				      struct thermal_genl_cpu_caps *caps)
405e4b1eb24SSrinivas Pandruvada {
406e4b1eb24SSrinivas Pandruvada 	struct param p = { .cpu_capabilities_count = count, .cpu_capabilities = caps };
407e4b1eb24SSrinivas Pandruvada 
408e4b1eb24SSrinivas Pandruvada 	return thermal_genl_send_event(THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE, &p);
409e4b1eb24SSrinivas Pandruvada }
410e4b1eb24SSrinivas Pandruvada EXPORT_SYMBOL_GPL(thermal_genl_cpu_capability_event);
411e4b1eb24SSrinivas Pandruvada 
4121ce50e7dSDaniel Lezcano /*************************** Command encoding ********************************/
4131ce50e7dSDaniel Lezcano 
4141ce50e7dSDaniel Lezcano static int __thermal_genl_cmd_tz_get_id(struct thermal_zone_device *tz,
4151ce50e7dSDaniel Lezcano 					void *data)
4161ce50e7dSDaniel Lezcano {
4171ce50e7dSDaniel Lezcano 	struct sk_buff *msg = data;
4181ce50e7dSDaniel Lezcano 
4191ce50e7dSDaniel Lezcano 	if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, tz->id) ||
4201ce50e7dSDaniel Lezcano 	    nla_put_string(msg, THERMAL_GENL_ATTR_TZ_NAME, tz->type))
4211ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
4221ce50e7dSDaniel Lezcano 
4231ce50e7dSDaniel Lezcano 	return 0;
4241ce50e7dSDaniel Lezcano }
4251ce50e7dSDaniel Lezcano 
4261ce50e7dSDaniel Lezcano static int thermal_genl_cmd_tz_get_id(struct param *p)
4271ce50e7dSDaniel Lezcano {
4281ce50e7dSDaniel Lezcano 	struct sk_buff *msg = p->msg;
4291ce50e7dSDaniel Lezcano 	struct nlattr *start_tz;
4301ce50e7dSDaniel Lezcano 	int ret;
4311ce50e7dSDaniel Lezcano 
4321ce50e7dSDaniel Lezcano 	start_tz = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ);
4331ce50e7dSDaniel Lezcano 	if (!start_tz)
4341ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
4351ce50e7dSDaniel Lezcano 
4361ce50e7dSDaniel Lezcano 	ret = for_each_thermal_zone(__thermal_genl_cmd_tz_get_id, msg);
4371ce50e7dSDaniel Lezcano 	if (ret)
4381ce50e7dSDaniel Lezcano 		goto out_cancel_nest;
4391ce50e7dSDaniel Lezcano 
4401ce50e7dSDaniel Lezcano 	nla_nest_end(msg, start_tz);
4411ce50e7dSDaniel Lezcano 
4421ce50e7dSDaniel Lezcano 	return 0;
4431ce50e7dSDaniel Lezcano 
4441ce50e7dSDaniel Lezcano out_cancel_nest:
4451ce50e7dSDaniel Lezcano 	nla_nest_cancel(msg, start_tz);
4461ce50e7dSDaniel Lezcano 
4471ce50e7dSDaniel Lezcano 	return ret;
4481ce50e7dSDaniel Lezcano }
4491ce50e7dSDaniel Lezcano 
4501ce50e7dSDaniel Lezcano static int thermal_genl_cmd_tz_get_trip(struct param *p)
4511ce50e7dSDaniel Lezcano {
4521ce50e7dSDaniel Lezcano 	struct sk_buff *msg = p->msg;
4531ce50e7dSDaniel Lezcano 	struct thermal_zone_device *tz;
4541ce50e7dSDaniel Lezcano 	struct nlattr *start_trip;
4551ce50e7dSDaniel Lezcano 	int i, id;
4561ce50e7dSDaniel Lezcano 
4571ce50e7dSDaniel Lezcano 	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
4581ce50e7dSDaniel Lezcano 		return -EINVAL;
4591ce50e7dSDaniel Lezcano 
4601ce50e7dSDaniel Lezcano 	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
4611ce50e7dSDaniel Lezcano 
4621ce50e7dSDaniel Lezcano 	tz = thermal_zone_get_by_id(id);
4631ce50e7dSDaniel Lezcano 	if (!tz)
4641ce50e7dSDaniel Lezcano 		return -EINVAL;
4651ce50e7dSDaniel Lezcano 
4661ce50e7dSDaniel Lezcano 	start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ_TRIP);
4671ce50e7dSDaniel Lezcano 	if (!start_trip)
4681ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
4691ce50e7dSDaniel Lezcano 
4701ce50e7dSDaniel Lezcano 	mutex_lock(&tz->lock);
4711ce50e7dSDaniel Lezcano 
472*e5bfcd30SDaniel Lezcano 	for (i = 0; i < tz->num_trips; i++) {
4731ce50e7dSDaniel Lezcano 
4741ce50e7dSDaniel Lezcano 		enum thermal_trip_type type;
4755838a148SNicolas Cavallari 		int temp, hyst = 0;
4761ce50e7dSDaniel Lezcano 
4771ce50e7dSDaniel Lezcano 		tz->ops->get_trip_type(tz, i, &type);
4781ce50e7dSDaniel Lezcano 		tz->ops->get_trip_temp(tz, i, &temp);
4795838a148SNicolas Cavallari 		if (tz->ops->get_trip_hyst)
4801ce50e7dSDaniel Lezcano 			tz->ops->get_trip_hyst(tz, i, &hyst);
4811ce50e7dSDaniel Lezcano 
4821ce50e7dSDaniel Lezcano 		if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, i) ||
4831ce50e7dSDaniel Lezcano 		    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, type) ||
4841ce50e7dSDaniel Lezcano 		    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, temp) ||
4851ce50e7dSDaniel Lezcano 		    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, hyst))
4861ce50e7dSDaniel Lezcano 			goto out_cancel_nest;
4871ce50e7dSDaniel Lezcano 	}
4881ce50e7dSDaniel Lezcano 
4891ce50e7dSDaniel Lezcano 	mutex_unlock(&tz->lock);
4901ce50e7dSDaniel Lezcano 
4911ce50e7dSDaniel Lezcano 	nla_nest_end(msg, start_trip);
4921ce50e7dSDaniel Lezcano 
4931ce50e7dSDaniel Lezcano 	return 0;
4941ce50e7dSDaniel Lezcano 
4951ce50e7dSDaniel Lezcano out_cancel_nest:
4961ce50e7dSDaniel Lezcano 	mutex_unlock(&tz->lock);
4971ce50e7dSDaniel Lezcano 
4981ce50e7dSDaniel Lezcano 	return -EMSGSIZE;
4991ce50e7dSDaniel Lezcano }
5001ce50e7dSDaniel Lezcano 
5011ce50e7dSDaniel Lezcano static int thermal_genl_cmd_tz_get_temp(struct param *p)
5021ce50e7dSDaniel Lezcano {
5031ce50e7dSDaniel Lezcano 	struct sk_buff *msg = p->msg;
5041ce50e7dSDaniel Lezcano 	struct thermal_zone_device *tz;
5051ce50e7dSDaniel Lezcano 	int temp, ret, id;
5061ce50e7dSDaniel Lezcano 
5071ce50e7dSDaniel Lezcano 	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
5081ce50e7dSDaniel Lezcano 		return -EINVAL;
5091ce50e7dSDaniel Lezcano 
5101ce50e7dSDaniel Lezcano 	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
5111ce50e7dSDaniel Lezcano 
5121ce50e7dSDaniel Lezcano 	tz = thermal_zone_get_by_id(id);
5131ce50e7dSDaniel Lezcano 	if (!tz)
5141ce50e7dSDaniel Lezcano 		return -EINVAL;
5151ce50e7dSDaniel Lezcano 
5161ce50e7dSDaniel Lezcano 	ret = thermal_zone_get_temp(tz, &temp);
5171ce50e7dSDaniel Lezcano 	if (ret)
5181ce50e7dSDaniel Lezcano 		return ret;
5191ce50e7dSDaniel Lezcano 
5201ce50e7dSDaniel Lezcano 	if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
5211ce50e7dSDaniel Lezcano 	    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TEMP, temp))
5221ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
5231ce50e7dSDaniel Lezcano 
5241ce50e7dSDaniel Lezcano 	return 0;
5251ce50e7dSDaniel Lezcano }
5261ce50e7dSDaniel Lezcano 
5271ce50e7dSDaniel Lezcano static int thermal_genl_cmd_tz_get_gov(struct param *p)
5281ce50e7dSDaniel Lezcano {
5291ce50e7dSDaniel Lezcano 	struct sk_buff *msg = p->msg;
5301ce50e7dSDaniel Lezcano 	struct thermal_zone_device *tz;
5311ce50e7dSDaniel Lezcano 	int id, ret = 0;
5321ce50e7dSDaniel Lezcano 
5331ce50e7dSDaniel Lezcano 	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
5341ce50e7dSDaniel Lezcano 		return -EINVAL;
5351ce50e7dSDaniel Lezcano 
5361ce50e7dSDaniel Lezcano 	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
5371ce50e7dSDaniel Lezcano 
5381ce50e7dSDaniel Lezcano 	tz = thermal_zone_get_by_id(id);
5391ce50e7dSDaniel Lezcano 	if (!tz)
5401ce50e7dSDaniel Lezcano 		return -EINVAL;
5411ce50e7dSDaniel Lezcano 
5421ce50e7dSDaniel Lezcano 	mutex_lock(&tz->lock);
5431ce50e7dSDaniel Lezcano 
5441ce50e7dSDaniel Lezcano 	if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
5451ce50e7dSDaniel Lezcano 	    nla_put_string(msg, THERMAL_GENL_ATTR_TZ_GOV_NAME,
5461ce50e7dSDaniel Lezcano 			   tz->governor->name))
5471ce50e7dSDaniel Lezcano 		ret = -EMSGSIZE;
5481ce50e7dSDaniel Lezcano 
5491ce50e7dSDaniel Lezcano 	mutex_unlock(&tz->lock);
5501ce50e7dSDaniel Lezcano 
5511ce50e7dSDaniel Lezcano 	return ret;
5521ce50e7dSDaniel Lezcano }
5531ce50e7dSDaniel Lezcano 
5541ce50e7dSDaniel Lezcano static int __thermal_genl_cmd_cdev_get(struct thermal_cooling_device *cdev,
5551ce50e7dSDaniel Lezcano 				       void *data)
5561ce50e7dSDaniel Lezcano {
5571ce50e7dSDaniel Lezcano 	struct sk_buff *msg = data;
5581ce50e7dSDaniel Lezcano 
5591ce50e7dSDaniel Lezcano 	if (nla_put_u32(msg, THERMAL_GENL_ATTR_CDEV_ID, cdev->id))
5601ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
5611ce50e7dSDaniel Lezcano 
5621ce50e7dSDaniel Lezcano 	if (nla_put_string(msg, THERMAL_GENL_ATTR_CDEV_NAME, cdev->type))
5631ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
5641ce50e7dSDaniel Lezcano 
5651ce50e7dSDaniel Lezcano 	return 0;
5661ce50e7dSDaniel Lezcano }
5671ce50e7dSDaniel Lezcano 
5681ce50e7dSDaniel Lezcano static int thermal_genl_cmd_cdev_get(struct param *p)
5691ce50e7dSDaniel Lezcano {
5701ce50e7dSDaniel Lezcano 	struct sk_buff *msg = p->msg;
5711ce50e7dSDaniel Lezcano 	struct nlattr *start_cdev;
5721ce50e7dSDaniel Lezcano 	int ret;
5731ce50e7dSDaniel Lezcano 
5741ce50e7dSDaniel Lezcano 	start_cdev = nla_nest_start(msg, THERMAL_GENL_ATTR_CDEV);
5751ce50e7dSDaniel Lezcano 	if (!start_cdev)
5761ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
5771ce50e7dSDaniel Lezcano 
5781ce50e7dSDaniel Lezcano 	ret = for_each_thermal_cooling_device(__thermal_genl_cmd_cdev_get, msg);
5791ce50e7dSDaniel Lezcano 	if (ret)
5801ce50e7dSDaniel Lezcano 		goto out_cancel_nest;
5811ce50e7dSDaniel Lezcano 
5821ce50e7dSDaniel Lezcano 	nla_nest_end(msg, start_cdev);
5831ce50e7dSDaniel Lezcano 
5841ce50e7dSDaniel Lezcano 	return 0;
5851ce50e7dSDaniel Lezcano out_cancel_nest:
5861ce50e7dSDaniel Lezcano 	nla_nest_cancel(msg, start_cdev);
5871ce50e7dSDaniel Lezcano 
5881ce50e7dSDaniel Lezcano 	return ret;
5891ce50e7dSDaniel Lezcano }
5901ce50e7dSDaniel Lezcano 
5911ce50e7dSDaniel Lezcano static cb_t cmd_cb[] = {
5921ce50e7dSDaniel Lezcano 	[THERMAL_GENL_CMD_TZ_GET_ID]	= thermal_genl_cmd_tz_get_id,
5931ce50e7dSDaniel Lezcano 	[THERMAL_GENL_CMD_TZ_GET_TRIP]	= thermal_genl_cmd_tz_get_trip,
5941ce50e7dSDaniel Lezcano 	[THERMAL_GENL_CMD_TZ_GET_TEMP]	= thermal_genl_cmd_tz_get_temp,
5951ce50e7dSDaniel Lezcano 	[THERMAL_GENL_CMD_TZ_GET_GOV]	= thermal_genl_cmd_tz_get_gov,
5961ce50e7dSDaniel Lezcano 	[THERMAL_GENL_CMD_CDEV_GET]	= thermal_genl_cmd_cdev_get,
5971ce50e7dSDaniel Lezcano };
5981ce50e7dSDaniel Lezcano 
5991ce50e7dSDaniel Lezcano static int thermal_genl_cmd_dumpit(struct sk_buff *skb,
6001ce50e7dSDaniel Lezcano 				   struct netlink_callback *cb)
6011ce50e7dSDaniel Lezcano {
6021ce50e7dSDaniel Lezcano 	struct param p = { .msg = skb };
6031ce50e7dSDaniel Lezcano 	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
6040b588afdSJakub Kicinski 	int cmd = info->op.cmd;
60552674f56SColin Ian King 	int ret;
6061ce50e7dSDaniel Lezcano 	void *hdr;
6071ce50e7dSDaniel Lezcano 
6081ce50e7dSDaniel Lezcano 	hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0, cmd);
6091ce50e7dSDaniel Lezcano 	if (!hdr)
6101ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
6111ce50e7dSDaniel Lezcano 
6121ce50e7dSDaniel Lezcano 	ret = cmd_cb[cmd](&p);
6131ce50e7dSDaniel Lezcano 	if (ret)
6141ce50e7dSDaniel Lezcano 		goto out_cancel_msg;
6151ce50e7dSDaniel Lezcano 
6161ce50e7dSDaniel Lezcano 	genlmsg_end(skb, hdr);
6171ce50e7dSDaniel Lezcano 
6181ce50e7dSDaniel Lezcano 	return 0;
6191ce50e7dSDaniel Lezcano 
6201ce50e7dSDaniel Lezcano out_cancel_msg:
6211ce50e7dSDaniel Lezcano 	genlmsg_cancel(skb, hdr);
6221ce50e7dSDaniel Lezcano 
6231ce50e7dSDaniel Lezcano 	return ret;
6241ce50e7dSDaniel Lezcano }
6251ce50e7dSDaniel Lezcano 
6261ce50e7dSDaniel Lezcano static int thermal_genl_cmd_doit(struct sk_buff *skb,
6271ce50e7dSDaniel Lezcano 				 struct genl_info *info)
6281ce50e7dSDaniel Lezcano {
6291ce50e7dSDaniel Lezcano 	struct param p = { .attrs = info->attrs };
6301ce50e7dSDaniel Lezcano 	struct sk_buff *msg;
6311ce50e7dSDaniel Lezcano 	void *hdr;
6321ce50e7dSDaniel Lezcano 	int cmd = info->genlhdr->cmd;
6331ce50e7dSDaniel Lezcano 	int ret = -EMSGSIZE;
6341ce50e7dSDaniel Lezcano 
6351ce50e7dSDaniel Lezcano 	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
6361ce50e7dSDaniel Lezcano 	if (!msg)
6371ce50e7dSDaniel Lezcano 		return -ENOMEM;
6381ce50e7dSDaniel Lezcano 	p.msg = msg;
6391ce50e7dSDaniel Lezcano 
6401ce50e7dSDaniel Lezcano 	hdr = genlmsg_put_reply(msg, info, &thermal_gnl_family, 0, cmd);
6411ce50e7dSDaniel Lezcano 	if (!hdr)
6421ce50e7dSDaniel Lezcano 		goto out_free_msg;
6431ce50e7dSDaniel Lezcano 
6441ce50e7dSDaniel Lezcano 	ret = cmd_cb[cmd](&p);
6451ce50e7dSDaniel Lezcano 	if (ret)
6461ce50e7dSDaniel Lezcano 		goto out_cancel_msg;
6471ce50e7dSDaniel Lezcano 
6481ce50e7dSDaniel Lezcano 	genlmsg_end(msg, hdr);
6491ce50e7dSDaniel Lezcano 
6501ce50e7dSDaniel Lezcano 	return genlmsg_reply(msg, info);
6511ce50e7dSDaniel Lezcano 
6521ce50e7dSDaniel Lezcano out_cancel_msg:
6531ce50e7dSDaniel Lezcano 	genlmsg_cancel(msg, hdr);
6541ce50e7dSDaniel Lezcano out_free_msg:
6551ce50e7dSDaniel Lezcano 	nlmsg_free(msg);
6561ce50e7dSDaniel Lezcano 
6571ce50e7dSDaniel Lezcano 	return ret;
6581ce50e7dSDaniel Lezcano }
6591ce50e7dSDaniel Lezcano 
66066a9b928SJakub Kicinski static const struct genl_small_ops thermal_genl_ops[] = {
6611ce50e7dSDaniel Lezcano 	{
6621ce50e7dSDaniel Lezcano 		.cmd = THERMAL_GENL_CMD_TZ_GET_ID,
6631ce50e7dSDaniel Lezcano 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
6641ce50e7dSDaniel Lezcano 		.dumpit = thermal_genl_cmd_dumpit,
6651ce50e7dSDaniel Lezcano 	},
6661ce50e7dSDaniel Lezcano 	{
6671ce50e7dSDaniel Lezcano 		.cmd = THERMAL_GENL_CMD_TZ_GET_TRIP,
6681ce50e7dSDaniel Lezcano 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
6691ce50e7dSDaniel Lezcano 		.doit = thermal_genl_cmd_doit,
6701ce50e7dSDaniel Lezcano 	},
6711ce50e7dSDaniel Lezcano 	{
6721ce50e7dSDaniel Lezcano 		.cmd = THERMAL_GENL_CMD_TZ_GET_TEMP,
6731ce50e7dSDaniel Lezcano 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
6741ce50e7dSDaniel Lezcano 		.doit = thermal_genl_cmd_doit,
6751ce50e7dSDaniel Lezcano 	},
6761ce50e7dSDaniel Lezcano 	{
6771ce50e7dSDaniel Lezcano 		.cmd = THERMAL_GENL_CMD_TZ_GET_GOV,
6781ce50e7dSDaniel Lezcano 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
6791ce50e7dSDaniel Lezcano 		.doit = thermal_genl_cmd_doit,
6801ce50e7dSDaniel Lezcano 	},
6811ce50e7dSDaniel Lezcano 	{
6821ce50e7dSDaniel Lezcano 		.cmd = THERMAL_GENL_CMD_CDEV_GET,
6831ce50e7dSDaniel Lezcano 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
6841ce50e7dSDaniel Lezcano 		.dumpit = thermal_genl_cmd_dumpit,
6851ce50e7dSDaniel Lezcano 	},
6861ce50e7dSDaniel Lezcano };
6871ce50e7dSDaniel Lezcano 
6881ce50e7dSDaniel Lezcano static struct genl_family thermal_gnl_family __ro_after_init = {
6891ce50e7dSDaniel Lezcano 	.hdrsize	= 0,
6901ce50e7dSDaniel Lezcano 	.name		= THERMAL_GENL_FAMILY_NAME,
6911ce50e7dSDaniel Lezcano 	.version	= THERMAL_GENL_VERSION,
6921ce50e7dSDaniel Lezcano 	.maxattr	= THERMAL_GENL_ATTR_MAX,
6931ce50e7dSDaniel Lezcano 	.policy		= thermal_genl_policy,
69466a9b928SJakub Kicinski 	.small_ops	= thermal_genl_ops,
69566a9b928SJakub Kicinski 	.n_small_ops	= ARRAY_SIZE(thermal_genl_ops),
6961ce50e7dSDaniel Lezcano 	.mcgrps		= thermal_genl_mcgrps,
6971ce50e7dSDaniel Lezcano 	.n_mcgrps	= ARRAY_SIZE(thermal_genl_mcgrps),
6981ce50e7dSDaniel Lezcano };
6991ce50e7dSDaniel Lezcano 
700d2a89b52SDaniel Lezcano int __init thermal_netlink_init(void)
7011ce50e7dSDaniel Lezcano {
7021ce50e7dSDaniel Lezcano 	return genl_register_family(&thermal_gnl_family);
7031ce50e7dSDaniel Lezcano }
704