xref: /openbmc/linux/drivers/thermal/thermal_netlink.c (revision fc656fa14da7865774b4251afa88ffcf22bf02d2)
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 },
461ce50e7dSDaniel Lezcano };
471ce50e7dSDaniel Lezcano 
481ce50e7dSDaniel Lezcano struct param {
491ce50e7dSDaniel Lezcano 	struct nlattr **attrs;
501ce50e7dSDaniel Lezcano 	struct sk_buff *msg;
511ce50e7dSDaniel Lezcano 	const char *name;
521ce50e7dSDaniel Lezcano 	int tz_id;
531ce50e7dSDaniel Lezcano 	int cdev_id;
541ce50e7dSDaniel Lezcano 	int trip_id;
551ce50e7dSDaniel Lezcano 	int trip_temp;
561ce50e7dSDaniel Lezcano 	int trip_type;
571ce50e7dSDaniel Lezcano 	int trip_hyst;
581ce50e7dSDaniel Lezcano 	int temp;
591ce50e7dSDaniel Lezcano 	int cdev_state;
601ce50e7dSDaniel Lezcano 	int cdev_max_state;
611ce50e7dSDaniel Lezcano };
621ce50e7dSDaniel Lezcano 
631ce50e7dSDaniel Lezcano typedef int (*cb_t)(struct param *);
641ce50e7dSDaniel Lezcano 
651ce50e7dSDaniel Lezcano static struct genl_family thermal_gnl_family;
661ce50e7dSDaniel Lezcano 
671ce50e7dSDaniel Lezcano /************************** Sampling encoding *******************************/
681ce50e7dSDaniel Lezcano 
691ce50e7dSDaniel Lezcano int thermal_genl_sampling_temp(int id, int temp)
701ce50e7dSDaniel Lezcano {
711ce50e7dSDaniel Lezcano 	struct sk_buff *skb;
721ce50e7dSDaniel Lezcano 	void *hdr;
731ce50e7dSDaniel Lezcano 
741ce50e7dSDaniel Lezcano 	skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
751ce50e7dSDaniel Lezcano 	if (!skb)
761ce50e7dSDaniel Lezcano 		return -ENOMEM;
771ce50e7dSDaniel Lezcano 
781ce50e7dSDaniel Lezcano 	hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0,
791ce50e7dSDaniel Lezcano 			  THERMAL_GENL_SAMPLING_TEMP);
801ce50e7dSDaniel Lezcano 	if (!hdr)
8148b45859SJing Xiangfeng 		goto out_free;
821ce50e7dSDaniel Lezcano 
831ce50e7dSDaniel Lezcano 	if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_ID, id))
841ce50e7dSDaniel Lezcano 		goto out_cancel;
851ce50e7dSDaniel Lezcano 
861ce50e7dSDaniel Lezcano 	if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_TEMP, temp))
871ce50e7dSDaniel Lezcano 		goto out_cancel;
881ce50e7dSDaniel Lezcano 
891ce50e7dSDaniel Lezcano 	genlmsg_end(skb, hdr);
901ce50e7dSDaniel Lezcano 
911ce50e7dSDaniel Lezcano 	genlmsg_multicast(&thermal_gnl_family, skb, 0, 0, GFP_KERNEL);
921ce50e7dSDaniel Lezcano 
931ce50e7dSDaniel Lezcano 	return 0;
941ce50e7dSDaniel Lezcano out_cancel:
951ce50e7dSDaniel Lezcano 	genlmsg_cancel(skb, hdr);
9648b45859SJing Xiangfeng out_free:
971ce50e7dSDaniel Lezcano 	nlmsg_free(skb);
981ce50e7dSDaniel Lezcano 
991ce50e7dSDaniel Lezcano 	return -EMSGSIZE;
1001ce50e7dSDaniel Lezcano }
1011ce50e7dSDaniel Lezcano 
1021ce50e7dSDaniel Lezcano /**************************** Event encoding *********************************/
1031ce50e7dSDaniel Lezcano 
1041ce50e7dSDaniel Lezcano static int thermal_genl_event_tz_create(struct param *p)
1051ce50e7dSDaniel Lezcano {
1061ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
1071ce50e7dSDaniel Lezcano 	    nla_put_string(p->msg, THERMAL_GENL_ATTR_TZ_NAME, p->name))
1081ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
1091ce50e7dSDaniel Lezcano 
1101ce50e7dSDaniel Lezcano 	return 0;
1111ce50e7dSDaniel Lezcano }
1121ce50e7dSDaniel Lezcano 
1131ce50e7dSDaniel Lezcano static int thermal_genl_event_tz(struct param *p)
1141ce50e7dSDaniel Lezcano {
1151ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id))
1161ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
1171ce50e7dSDaniel Lezcano 
1181ce50e7dSDaniel Lezcano 	return 0;
1191ce50e7dSDaniel Lezcano }
1201ce50e7dSDaniel Lezcano 
1211ce50e7dSDaniel Lezcano static int thermal_genl_event_tz_trip_up(struct param *p)
1221ce50e7dSDaniel Lezcano {
1231ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
124*fc656fa1SDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id) ||
125*fc656fa1SDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TEMP, p->temp))
1261ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
1271ce50e7dSDaniel Lezcano 
1281ce50e7dSDaniel Lezcano 	return 0;
1291ce50e7dSDaniel Lezcano }
1301ce50e7dSDaniel Lezcano 
1311ce50e7dSDaniel Lezcano static int thermal_genl_event_tz_trip_add(struct param *p)
1321ce50e7dSDaniel Lezcano {
1331ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
1341ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id) ||
1351ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, p->trip_type) ||
1361ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, p->trip_temp) ||
1371ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, p->trip_hyst))
1381ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
1391ce50e7dSDaniel Lezcano 
1401ce50e7dSDaniel Lezcano 	return 0;
1411ce50e7dSDaniel Lezcano }
1421ce50e7dSDaniel Lezcano 
1431ce50e7dSDaniel Lezcano static int thermal_genl_event_tz_trip_delete(struct param *p)
1441ce50e7dSDaniel Lezcano {
1451ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
1461ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id))
1471ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
1481ce50e7dSDaniel Lezcano 
1491ce50e7dSDaniel Lezcano 	return 0;
1501ce50e7dSDaniel Lezcano }
1511ce50e7dSDaniel Lezcano 
1521ce50e7dSDaniel Lezcano static int thermal_genl_event_cdev_add(struct param *p)
1531ce50e7dSDaniel Lezcano {
1541ce50e7dSDaniel Lezcano 	if (nla_put_string(p->msg, THERMAL_GENL_ATTR_CDEV_NAME,
1551ce50e7dSDaniel Lezcano 			   p->name) ||
1561ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID,
1571ce50e7dSDaniel Lezcano 			p->cdev_id) ||
1581ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_MAX_STATE,
1591ce50e7dSDaniel Lezcano 			p->cdev_max_state))
1601ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
1611ce50e7dSDaniel Lezcano 
1621ce50e7dSDaniel Lezcano 	return 0;
1631ce50e7dSDaniel Lezcano }
1641ce50e7dSDaniel Lezcano 
1651ce50e7dSDaniel Lezcano static int thermal_genl_event_cdev_delete(struct param *p)
1661ce50e7dSDaniel Lezcano {
1671ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID, p->cdev_id))
1681ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
1691ce50e7dSDaniel Lezcano 
1701ce50e7dSDaniel Lezcano 	return 0;
1711ce50e7dSDaniel Lezcano }
1721ce50e7dSDaniel Lezcano 
1731ce50e7dSDaniel Lezcano static int thermal_genl_event_cdev_state_update(struct param *p)
1741ce50e7dSDaniel Lezcano {
1751ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID,
1761ce50e7dSDaniel Lezcano 			p->cdev_id) ||
1771ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_CUR_STATE,
1781ce50e7dSDaniel Lezcano 			p->cdev_state))
1791ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
1801ce50e7dSDaniel Lezcano 
1811ce50e7dSDaniel Lezcano 	return 0;
1821ce50e7dSDaniel Lezcano }
1831ce50e7dSDaniel Lezcano 
1841ce50e7dSDaniel Lezcano static int thermal_genl_event_gov_change(struct param *p)
1851ce50e7dSDaniel Lezcano {
1861ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
1871ce50e7dSDaniel Lezcano 	    nla_put_string(p->msg, THERMAL_GENL_ATTR_GOV_NAME, p->name))
1881ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
1891ce50e7dSDaniel Lezcano 
1901ce50e7dSDaniel Lezcano 	return 0;
1911ce50e7dSDaniel Lezcano }
1921ce50e7dSDaniel Lezcano 
1931ce50e7dSDaniel Lezcano int thermal_genl_event_tz_delete(struct param *p)
1941ce50e7dSDaniel Lezcano 	__attribute__((alias("thermal_genl_event_tz")));
1951ce50e7dSDaniel Lezcano 
1961ce50e7dSDaniel Lezcano int thermal_genl_event_tz_enable(struct param *p)
1971ce50e7dSDaniel Lezcano 	__attribute__((alias("thermal_genl_event_tz")));
1981ce50e7dSDaniel Lezcano 
1991ce50e7dSDaniel Lezcano int thermal_genl_event_tz_disable(struct param *p)
2001ce50e7dSDaniel Lezcano 	__attribute__((alias("thermal_genl_event_tz")));
2011ce50e7dSDaniel Lezcano 
2021ce50e7dSDaniel Lezcano int thermal_genl_event_tz_trip_down(struct param *p)
2031ce50e7dSDaniel Lezcano 	__attribute__((alias("thermal_genl_event_tz_trip_up")));
2041ce50e7dSDaniel Lezcano 
2051ce50e7dSDaniel Lezcano int thermal_genl_event_tz_trip_change(struct param *p)
2061ce50e7dSDaniel Lezcano 	__attribute__((alias("thermal_genl_event_tz_trip_add")));
2071ce50e7dSDaniel Lezcano 
2081ce50e7dSDaniel Lezcano static cb_t event_cb[] = {
2091ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_CREATE]		= thermal_genl_event_tz_create,
2101ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_DELETE]		= thermal_genl_event_tz_delete,
2111ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_ENABLE]		= thermal_genl_event_tz_enable,
2121ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_DISABLE]		= thermal_genl_event_tz_disable,
2131ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_TRIP_UP]		= thermal_genl_event_tz_trip_up,
2141ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_TRIP_DOWN]	= thermal_genl_event_tz_trip_down,
2151ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_TRIP_CHANGE]	= thermal_genl_event_tz_trip_change,
2161ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_TRIP_ADD]	= thermal_genl_event_tz_trip_add,
2171ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_TRIP_DELETE]	= thermal_genl_event_tz_trip_delete,
2181ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_CDEV_ADD]		= thermal_genl_event_cdev_add,
2191ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_CDEV_DELETE]	= thermal_genl_event_cdev_delete,
2201ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE]	= thermal_genl_event_cdev_state_update,
2211ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_GOV_CHANGE]	= thermal_genl_event_gov_change,
2221ce50e7dSDaniel Lezcano };
2231ce50e7dSDaniel Lezcano 
2241ce50e7dSDaniel Lezcano /*
2251ce50e7dSDaniel Lezcano  * Generic netlink event encoding
2261ce50e7dSDaniel Lezcano  */
2271ce50e7dSDaniel Lezcano static int thermal_genl_send_event(enum thermal_genl_event event,
2281ce50e7dSDaniel Lezcano 				   struct param *p)
2291ce50e7dSDaniel Lezcano {
2301ce50e7dSDaniel Lezcano 	struct sk_buff *msg;
2311ce50e7dSDaniel Lezcano 	int ret = -EMSGSIZE;
2321ce50e7dSDaniel Lezcano 	void *hdr;
2331ce50e7dSDaniel Lezcano 
2341ce50e7dSDaniel Lezcano 	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
2351ce50e7dSDaniel Lezcano 	if (!msg)
2361ce50e7dSDaniel Lezcano 		return -ENOMEM;
2371ce50e7dSDaniel Lezcano 	p->msg = msg;
2381ce50e7dSDaniel Lezcano 
2391ce50e7dSDaniel Lezcano 	hdr = genlmsg_put(msg, 0, 0, &thermal_gnl_family, 0, event);
2401ce50e7dSDaniel Lezcano 	if (!hdr)
2411ce50e7dSDaniel Lezcano 		goto out_free_msg;
2421ce50e7dSDaniel Lezcano 
2431ce50e7dSDaniel Lezcano 	ret = event_cb[event](p);
2441ce50e7dSDaniel Lezcano 	if (ret)
2451ce50e7dSDaniel Lezcano 		goto out_cancel_msg;
2461ce50e7dSDaniel Lezcano 
2471ce50e7dSDaniel Lezcano 	genlmsg_end(msg, hdr);
2481ce50e7dSDaniel Lezcano 
2491ce50e7dSDaniel Lezcano 	genlmsg_multicast(&thermal_gnl_family, msg, 0, 1, GFP_KERNEL);
2501ce50e7dSDaniel Lezcano 
2511ce50e7dSDaniel Lezcano 	return 0;
2521ce50e7dSDaniel Lezcano 
2531ce50e7dSDaniel Lezcano out_cancel_msg:
2541ce50e7dSDaniel Lezcano 	genlmsg_cancel(msg, hdr);
2551ce50e7dSDaniel Lezcano out_free_msg:
2561ce50e7dSDaniel Lezcano 	nlmsg_free(msg);
2571ce50e7dSDaniel Lezcano 
2581ce50e7dSDaniel Lezcano 	return ret;
2591ce50e7dSDaniel Lezcano }
2601ce50e7dSDaniel Lezcano 
2611ce50e7dSDaniel Lezcano int thermal_notify_tz_create(int tz_id, const char *name)
2621ce50e7dSDaniel Lezcano {
2631ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id, .name = name };
2641ce50e7dSDaniel Lezcano 
2651ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_CREATE, &p);
2661ce50e7dSDaniel Lezcano }
2671ce50e7dSDaniel Lezcano 
2681ce50e7dSDaniel Lezcano int thermal_notify_tz_delete(int tz_id)
2691ce50e7dSDaniel Lezcano {
2701ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id };
2711ce50e7dSDaniel Lezcano 
2721ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DELETE, &p);
2731ce50e7dSDaniel Lezcano }
2741ce50e7dSDaniel Lezcano 
2751ce50e7dSDaniel Lezcano int thermal_notify_tz_enable(int tz_id)
2761ce50e7dSDaniel Lezcano {
2771ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id };
2781ce50e7dSDaniel Lezcano 
2791ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_ENABLE, &p);
2801ce50e7dSDaniel Lezcano }
2811ce50e7dSDaniel Lezcano 
2821ce50e7dSDaniel Lezcano int thermal_notify_tz_disable(int tz_id)
2831ce50e7dSDaniel Lezcano {
2841ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id };
2851ce50e7dSDaniel Lezcano 
2861ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DISABLE, &p);
2871ce50e7dSDaniel Lezcano }
2881ce50e7dSDaniel Lezcano 
289*fc656fa1SDaniel Lezcano int thermal_notify_tz_trip_down(int tz_id, int trip_id, int temp)
2901ce50e7dSDaniel Lezcano {
291*fc656fa1SDaniel Lezcano 	struct param p = { .tz_id = tz_id, .trip_id = trip_id, .temp = temp };
2921ce50e7dSDaniel Lezcano 
2931ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DOWN, &p);
2941ce50e7dSDaniel Lezcano }
2951ce50e7dSDaniel Lezcano 
296*fc656fa1SDaniel Lezcano int thermal_notify_tz_trip_up(int tz_id, int trip_id, int temp)
2971ce50e7dSDaniel Lezcano {
298*fc656fa1SDaniel Lezcano 	struct param p = { .tz_id = tz_id, .trip_id = trip_id, .temp = temp };
2991ce50e7dSDaniel Lezcano 
3001ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_UP, &p);
3011ce50e7dSDaniel Lezcano }
3021ce50e7dSDaniel Lezcano 
3031ce50e7dSDaniel Lezcano int thermal_notify_tz_trip_add(int tz_id, int trip_id, int trip_type,
3041ce50e7dSDaniel Lezcano 			       int trip_temp, int trip_hyst)
3051ce50e7dSDaniel Lezcano {
3061ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id, .trip_id = trip_id,
3071ce50e7dSDaniel Lezcano 			   .trip_type = trip_type, .trip_temp = trip_temp,
3081ce50e7dSDaniel Lezcano 			   .trip_hyst = trip_hyst };
3091ce50e7dSDaniel Lezcano 
3101ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_ADD, &p);
3111ce50e7dSDaniel Lezcano }
3121ce50e7dSDaniel Lezcano 
3131ce50e7dSDaniel Lezcano int thermal_notify_tz_trip_delete(int tz_id, int trip_id)
3141ce50e7dSDaniel Lezcano {
3151ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id, .trip_id = trip_id };
3161ce50e7dSDaniel Lezcano 
3171ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DELETE, &p);
3181ce50e7dSDaniel Lezcano }
3191ce50e7dSDaniel Lezcano 
3201ce50e7dSDaniel Lezcano int thermal_notify_tz_trip_change(int tz_id, int trip_id, int trip_type,
3211ce50e7dSDaniel Lezcano 				  int trip_temp, int trip_hyst)
3221ce50e7dSDaniel Lezcano {
3231ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id, .trip_id = trip_id,
3241ce50e7dSDaniel Lezcano 			   .trip_type = trip_type, .trip_temp = trip_temp,
3251ce50e7dSDaniel Lezcano 			   .trip_hyst = trip_hyst };
3261ce50e7dSDaniel Lezcano 
3271ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_CHANGE, &p);
3281ce50e7dSDaniel Lezcano }
3291ce50e7dSDaniel Lezcano 
3301ce50e7dSDaniel Lezcano int thermal_notify_cdev_state_update(int cdev_id, int cdev_state)
3311ce50e7dSDaniel Lezcano {
3321ce50e7dSDaniel Lezcano 	struct param p = { .cdev_id = cdev_id, .cdev_state = cdev_state };
3331ce50e7dSDaniel Lezcano 
3341ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, &p);
3351ce50e7dSDaniel Lezcano }
3361ce50e7dSDaniel Lezcano 
3371ce50e7dSDaniel Lezcano int thermal_notify_cdev_add(int cdev_id, const char *name, int cdev_max_state)
3381ce50e7dSDaniel Lezcano {
3391ce50e7dSDaniel Lezcano 	struct param p = { .cdev_id = cdev_id, .name = name,
3401ce50e7dSDaniel Lezcano 			   .cdev_max_state = cdev_max_state };
3411ce50e7dSDaniel Lezcano 
3421ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_ADD, &p);
3431ce50e7dSDaniel Lezcano }
3441ce50e7dSDaniel Lezcano 
3451ce50e7dSDaniel Lezcano int thermal_notify_cdev_delete(int cdev_id)
3461ce50e7dSDaniel Lezcano {
3471ce50e7dSDaniel Lezcano 	struct param p = { .cdev_id = cdev_id };
3481ce50e7dSDaniel Lezcano 
3491ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_DELETE, &p);
3501ce50e7dSDaniel Lezcano }
3511ce50e7dSDaniel Lezcano 
3521ce50e7dSDaniel Lezcano int thermal_notify_tz_gov_change(int tz_id, const char *name)
3531ce50e7dSDaniel Lezcano {
3541ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id, .name = name };
3551ce50e7dSDaniel Lezcano 
3561ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_GOV_CHANGE, &p);
3571ce50e7dSDaniel Lezcano }
3581ce50e7dSDaniel Lezcano 
3591ce50e7dSDaniel Lezcano /*************************** Command encoding ********************************/
3601ce50e7dSDaniel Lezcano 
3611ce50e7dSDaniel Lezcano static int __thermal_genl_cmd_tz_get_id(struct thermal_zone_device *tz,
3621ce50e7dSDaniel Lezcano 					void *data)
3631ce50e7dSDaniel Lezcano {
3641ce50e7dSDaniel Lezcano 	struct sk_buff *msg = data;
3651ce50e7dSDaniel Lezcano 
3661ce50e7dSDaniel Lezcano 	if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, tz->id) ||
3671ce50e7dSDaniel Lezcano 	    nla_put_string(msg, THERMAL_GENL_ATTR_TZ_NAME, tz->type))
3681ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
3691ce50e7dSDaniel Lezcano 
3701ce50e7dSDaniel Lezcano 	return 0;
3711ce50e7dSDaniel Lezcano }
3721ce50e7dSDaniel Lezcano 
3731ce50e7dSDaniel Lezcano static int thermal_genl_cmd_tz_get_id(struct param *p)
3741ce50e7dSDaniel Lezcano {
3751ce50e7dSDaniel Lezcano 	struct sk_buff *msg = p->msg;
3761ce50e7dSDaniel Lezcano 	struct nlattr *start_tz;
3771ce50e7dSDaniel Lezcano 	int ret;
3781ce50e7dSDaniel Lezcano 
3791ce50e7dSDaniel Lezcano 	start_tz = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ);
3801ce50e7dSDaniel Lezcano 	if (!start_tz)
3811ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
3821ce50e7dSDaniel Lezcano 
3831ce50e7dSDaniel Lezcano 	ret = for_each_thermal_zone(__thermal_genl_cmd_tz_get_id, msg);
3841ce50e7dSDaniel Lezcano 	if (ret)
3851ce50e7dSDaniel Lezcano 		goto out_cancel_nest;
3861ce50e7dSDaniel Lezcano 
3871ce50e7dSDaniel Lezcano 	nla_nest_end(msg, start_tz);
3881ce50e7dSDaniel Lezcano 
3891ce50e7dSDaniel Lezcano 	return 0;
3901ce50e7dSDaniel Lezcano 
3911ce50e7dSDaniel Lezcano out_cancel_nest:
3921ce50e7dSDaniel Lezcano 	nla_nest_cancel(msg, start_tz);
3931ce50e7dSDaniel Lezcano 
3941ce50e7dSDaniel Lezcano 	return ret;
3951ce50e7dSDaniel Lezcano }
3961ce50e7dSDaniel Lezcano 
3971ce50e7dSDaniel Lezcano static int thermal_genl_cmd_tz_get_trip(struct param *p)
3981ce50e7dSDaniel Lezcano {
3991ce50e7dSDaniel Lezcano 	struct sk_buff *msg = p->msg;
4001ce50e7dSDaniel Lezcano 	struct thermal_zone_device *tz;
4011ce50e7dSDaniel Lezcano 	struct nlattr *start_trip;
4021ce50e7dSDaniel Lezcano 	int i, id;
4031ce50e7dSDaniel Lezcano 
4041ce50e7dSDaniel Lezcano 	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
4051ce50e7dSDaniel Lezcano 		return -EINVAL;
4061ce50e7dSDaniel Lezcano 
4071ce50e7dSDaniel Lezcano 	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
4081ce50e7dSDaniel Lezcano 
4091ce50e7dSDaniel Lezcano 	tz = thermal_zone_get_by_id(id);
4101ce50e7dSDaniel Lezcano 	if (!tz)
4111ce50e7dSDaniel Lezcano 		return -EINVAL;
4121ce50e7dSDaniel Lezcano 
4131ce50e7dSDaniel Lezcano 	start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ_TRIP);
4141ce50e7dSDaniel Lezcano 	if (!start_trip)
4151ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
4161ce50e7dSDaniel Lezcano 
4171ce50e7dSDaniel Lezcano 	mutex_lock(&tz->lock);
4181ce50e7dSDaniel Lezcano 
4191ce50e7dSDaniel Lezcano 	for (i = 0; i < tz->trips; i++) {
4201ce50e7dSDaniel Lezcano 
4211ce50e7dSDaniel Lezcano 		enum thermal_trip_type type;
4221ce50e7dSDaniel Lezcano 		int temp, hyst;
4231ce50e7dSDaniel Lezcano 
4241ce50e7dSDaniel Lezcano 		tz->ops->get_trip_type(tz, i, &type);
4251ce50e7dSDaniel Lezcano 		tz->ops->get_trip_temp(tz, i, &temp);
4261ce50e7dSDaniel Lezcano 		tz->ops->get_trip_hyst(tz, i, &hyst);
4271ce50e7dSDaniel Lezcano 
4281ce50e7dSDaniel Lezcano 		if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, i) ||
4291ce50e7dSDaniel Lezcano 		    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, type) ||
4301ce50e7dSDaniel Lezcano 		    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, temp) ||
4311ce50e7dSDaniel Lezcano 		    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, hyst))
4321ce50e7dSDaniel Lezcano 			goto out_cancel_nest;
4331ce50e7dSDaniel Lezcano 	}
4341ce50e7dSDaniel Lezcano 
4351ce50e7dSDaniel Lezcano 	mutex_unlock(&tz->lock);
4361ce50e7dSDaniel Lezcano 
4371ce50e7dSDaniel Lezcano 	nla_nest_end(msg, start_trip);
4381ce50e7dSDaniel Lezcano 
4391ce50e7dSDaniel Lezcano 	return 0;
4401ce50e7dSDaniel Lezcano 
4411ce50e7dSDaniel Lezcano out_cancel_nest:
4421ce50e7dSDaniel Lezcano 	mutex_unlock(&tz->lock);
4431ce50e7dSDaniel Lezcano 
4441ce50e7dSDaniel Lezcano 	return -EMSGSIZE;
4451ce50e7dSDaniel Lezcano }
4461ce50e7dSDaniel Lezcano 
4471ce50e7dSDaniel Lezcano static int thermal_genl_cmd_tz_get_temp(struct param *p)
4481ce50e7dSDaniel Lezcano {
4491ce50e7dSDaniel Lezcano 	struct sk_buff *msg = p->msg;
4501ce50e7dSDaniel Lezcano 	struct thermal_zone_device *tz;
4511ce50e7dSDaniel Lezcano 	int temp, ret, id;
4521ce50e7dSDaniel Lezcano 
4531ce50e7dSDaniel Lezcano 	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
4541ce50e7dSDaniel Lezcano 		return -EINVAL;
4551ce50e7dSDaniel Lezcano 
4561ce50e7dSDaniel Lezcano 	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
4571ce50e7dSDaniel Lezcano 
4581ce50e7dSDaniel Lezcano 	tz = thermal_zone_get_by_id(id);
4591ce50e7dSDaniel Lezcano 	if (!tz)
4601ce50e7dSDaniel Lezcano 		return -EINVAL;
4611ce50e7dSDaniel Lezcano 
4621ce50e7dSDaniel Lezcano 	ret = thermal_zone_get_temp(tz, &temp);
4631ce50e7dSDaniel Lezcano 	if (ret)
4641ce50e7dSDaniel Lezcano 		return ret;
4651ce50e7dSDaniel Lezcano 
4661ce50e7dSDaniel Lezcano 	if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
4671ce50e7dSDaniel Lezcano 	    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TEMP, temp))
4681ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
4691ce50e7dSDaniel Lezcano 
4701ce50e7dSDaniel Lezcano 	return 0;
4711ce50e7dSDaniel Lezcano }
4721ce50e7dSDaniel Lezcano 
4731ce50e7dSDaniel Lezcano static int thermal_genl_cmd_tz_get_gov(struct param *p)
4741ce50e7dSDaniel Lezcano {
4751ce50e7dSDaniel Lezcano 	struct sk_buff *msg = p->msg;
4761ce50e7dSDaniel Lezcano 	struct thermal_zone_device *tz;
4771ce50e7dSDaniel Lezcano 	int id, ret = 0;
4781ce50e7dSDaniel Lezcano 
4791ce50e7dSDaniel Lezcano 	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
4801ce50e7dSDaniel Lezcano 		return -EINVAL;
4811ce50e7dSDaniel Lezcano 
4821ce50e7dSDaniel Lezcano 	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
4831ce50e7dSDaniel Lezcano 
4841ce50e7dSDaniel Lezcano 	tz = thermal_zone_get_by_id(id);
4851ce50e7dSDaniel Lezcano 	if (!tz)
4861ce50e7dSDaniel Lezcano 		return -EINVAL;
4871ce50e7dSDaniel Lezcano 
4881ce50e7dSDaniel Lezcano 	mutex_lock(&tz->lock);
4891ce50e7dSDaniel Lezcano 
4901ce50e7dSDaniel Lezcano 	if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
4911ce50e7dSDaniel Lezcano 	    nla_put_string(msg, THERMAL_GENL_ATTR_TZ_GOV_NAME,
4921ce50e7dSDaniel Lezcano 			   tz->governor->name))
4931ce50e7dSDaniel Lezcano 		ret = -EMSGSIZE;
4941ce50e7dSDaniel Lezcano 
4951ce50e7dSDaniel Lezcano 	mutex_unlock(&tz->lock);
4961ce50e7dSDaniel Lezcano 
4971ce50e7dSDaniel Lezcano 	return ret;
4981ce50e7dSDaniel Lezcano }
4991ce50e7dSDaniel Lezcano 
5001ce50e7dSDaniel Lezcano static int __thermal_genl_cmd_cdev_get(struct thermal_cooling_device *cdev,
5011ce50e7dSDaniel Lezcano 				       void *data)
5021ce50e7dSDaniel Lezcano {
5031ce50e7dSDaniel Lezcano 	struct sk_buff *msg = data;
5041ce50e7dSDaniel Lezcano 
5051ce50e7dSDaniel Lezcano 	if (nla_put_u32(msg, THERMAL_GENL_ATTR_CDEV_ID, cdev->id))
5061ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
5071ce50e7dSDaniel Lezcano 
5081ce50e7dSDaniel Lezcano 	if (nla_put_string(msg, THERMAL_GENL_ATTR_CDEV_NAME, cdev->type))
5091ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
5101ce50e7dSDaniel Lezcano 
5111ce50e7dSDaniel Lezcano 	return 0;
5121ce50e7dSDaniel Lezcano }
5131ce50e7dSDaniel Lezcano 
5141ce50e7dSDaniel Lezcano static int thermal_genl_cmd_cdev_get(struct param *p)
5151ce50e7dSDaniel Lezcano {
5161ce50e7dSDaniel Lezcano 	struct sk_buff *msg = p->msg;
5171ce50e7dSDaniel Lezcano 	struct nlattr *start_cdev;
5181ce50e7dSDaniel Lezcano 	int ret;
5191ce50e7dSDaniel Lezcano 
5201ce50e7dSDaniel Lezcano 	start_cdev = nla_nest_start(msg, THERMAL_GENL_ATTR_CDEV);
5211ce50e7dSDaniel Lezcano 	if (!start_cdev)
5221ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
5231ce50e7dSDaniel Lezcano 
5241ce50e7dSDaniel Lezcano 	ret = for_each_thermal_cooling_device(__thermal_genl_cmd_cdev_get, msg);
5251ce50e7dSDaniel Lezcano 	if (ret)
5261ce50e7dSDaniel Lezcano 		goto out_cancel_nest;
5271ce50e7dSDaniel Lezcano 
5281ce50e7dSDaniel Lezcano 	nla_nest_end(msg, start_cdev);
5291ce50e7dSDaniel Lezcano 
5301ce50e7dSDaniel Lezcano 	return 0;
5311ce50e7dSDaniel Lezcano out_cancel_nest:
5321ce50e7dSDaniel Lezcano 	nla_nest_cancel(msg, start_cdev);
5331ce50e7dSDaniel Lezcano 
5341ce50e7dSDaniel Lezcano 	return ret;
5351ce50e7dSDaniel Lezcano }
5361ce50e7dSDaniel Lezcano 
5371ce50e7dSDaniel Lezcano static cb_t cmd_cb[] = {
5381ce50e7dSDaniel Lezcano 	[THERMAL_GENL_CMD_TZ_GET_ID]	= thermal_genl_cmd_tz_get_id,
5391ce50e7dSDaniel Lezcano 	[THERMAL_GENL_CMD_TZ_GET_TRIP]	= thermal_genl_cmd_tz_get_trip,
5401ce50e7dSDaniel Lezcano 	[THERMAL_GENL_CMD_TZ_GET_TEMP]	= thermal_genl_cmd_tz_get_temp,
5411ce50e7dSDaniel Lezcano 	[THERMAL_GENL_CMD_TZ_GET_GOV]	= thermal_genl_cmd_tz_get_gov,
5421ce50e7dSDaniel Lezcano 	[THERMAL_GENL_CMD_CDEV_GET]	= thermal_genl_cmd_cdev_get,
5431ce50e7dSDaniel Lezcano };
5441ce50e7dSDaniel Lezcano 
5451ce50e7dSDaniel Lezcano static int thermal_genl_cmd_dumpit(struct sk_buff *skb,
5461ce50e7dSDaniel Lezcano 				   struct netlink_callback *cb)
5471ce50e7dSDaniel Lezcano {
5481ce50e7dSDaniel Lezcano 	struct param p = { .msg = skb };
5491ce50e7dSDaniel Lezcano 	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
5500b588afdSJakub Kicinski 	int cmd = info->op.cmd;
55152674f56SColin Ian King 	int ret;
5521ce50e7dSDaniel Lezcano 	void *hdr;
5531ce50e7dSDaniel Lezcano 
5541ce50e7dSDaniel Lezcano 	hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0, cmd);
5551ce50e7dSDaniel Lezcano 	if (!hdr)
5561ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
5571ce50e7dSDaniel Lezcano 
5581ce50e7dSDaniel Lezcano 	ret = cmd_cb[cmd](&p);
5591ce50e7dSDaniel Lezcano 	if (ret)
5601ce50e7dSDaniel Lezcano 		goto out_cancel_msg;
5611ce50e7dSDaniel Lezcano 
5621ce50e7dSDaniel Lezcano 	genlmsg_end(skb, hdr);
5631ce50e7dSDaniel Lezcano 
5641ce50e7dSDaniel Lezcano 	return 0;
5651ce50e7dSDaniel Lezcano 
5661ce50e7dSDaniel Lezcano out_cancel_msg:
5671ce50e7dSDaniel Lezcano 	genlmsg_cancel(skb, hdr);
5681ce50e7dSDaniel Lezcano 
5691ce50e7dSDaniel Lezcano 	return ret;
5701ce50e7dSDaniel Lezcano }
5711ce50e7dSDaniel Lezcano 
5721ce50e7dSDaniel Lezcano static int thermal_genl_cmd_doit(struct sk_buff *skb,
5731ce50e7dSDaniel Lezcano 				 struct genl_info *info)
5741ce50e7dSDaniel Lezcano {
5751ce50e7dSDaniel Lezcano 	struct param p = { .attrs = info->attrs };
5761ce50e7dSDaniel Lezcano 	struct sk_buff *msg;
5771ce50e7dSDaniel Lezcano 	void *hdr;
5781ce50e7dSDaniel Lezcano 	int cmd = info->genlhdr->cmd;
5791ce50e7dSDaniel Lezcano 	int ret = -EMSGSIZE;
5801ce50e7dSDaniel Lezcano 
5811ce50e7dSDaniel Lezcano 	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
5821ce50e7dSDaniel Lezcano 	if (!msg)
5831ce50e7dSDaniel Lezcano 		return -ENOMEM;
5841ce50e7dSDaniel Lezcano 	p.msg = msg;
5851ce50e7dSDaniel Lezcano 
5861ce50e7dSDaniel Lezcano 	hdr = genlmsg_put_reply(msg, info, &thermal_gnl_family, 0, cmd);
5871ce50e7dSDaniel Lezcano 	if (!hdr)
5881ce50e7dSDaniel Lezcano 		goto out_free_msg;
5891ce50e7dSDaniel Lezcano 
5901ce50e7dSDaniel Lezcano 	ret = cmd_cb[cmd](&p);
5911ce50e7dSDaniel Lezcano 	if (ret)
5921ce50e7dSDaniel Lezcano 		goto out_cancel_msg;
5931ce50e7dSDaniel Lezcano 
5941ce50e7dSDaniel Lezcano 	genlmsg_end(msg, hdr);
5951ce50e7dSDaniel Lezcano 
5961ce50e7dSDaniel Lezcano 	return genlmsg_reply(msg, info);
5971ce50e7dSDaniel Lezcano 
5981ce50e7dSDaniel Lezcano out_cancel_msg:
5991ce50e7dSDaniel Lezcano 	genlmsg_cancel(msg, hdr);
6001ce50e7dSDaniel Lezcano out_free_msg:
6011ce50e7dSDaniel Lezcano 	nlmsg_free(msg);
6021ce50e7dSDaniel Lezcano 
6031ce50e7dSDaniel Lezcano 	return ret;
6041ce50e7dSDaniel Lezcano }
6051ce50e7dSDaniel Lezcano 
60666a9b928SJakub Kicinski static const struct genl_small_ops thermal_genl_ops[] = {
6071ce50e7dSDaniel Lezcano 	{
6081ce50e7dSDaniel Lezcano 		.cmd = THERMAL_GENL_CMD_TZ_GET_ID,
6091ce50e7dSDaniel Lezcano 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
6101ce50e7dSDaniel Lezcano 		.dumpit = thermal_genl_cmd_dumpit,
6111ce50e7dSDaniel Lezcano 	},
6121ce50e7dSDaniel Lezcano 	{
6131ce50e7dSDaniel Lezcano 		.cmd = THERMAL_GENL_CMD_TZ_GET_TRIP,
6141ce50e7dSDaniel Lezcano 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
6151ce50e7dSDaniel Lezcano 		.doit = thermal_genl_cmd_doit,
6161ce50e7dSDaniel Lezcano 	},
6171ce50e7dSDaniel Lezcano 	{
6181ce50e7dSDaniel Lezcano 		.cmd = THERMAL_GENL_CMD_TZ_GET_TEMP,
6191ce50e7dSDaniel Lezcano 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
6201ce50e7dSDaniel Lezcano 		.doit = thermal_genl_cmd_doit,
6211ce50e7dSDaniel Lezcano 	},
6221ce50e7dSDaniel Lezcano 	{
6231ce50e7dSDaniel Lezcano 		.cmd = THERMAL_GENL_CMD_TZ_GET_GOV,
6241ce50e7dSDaniel Lezcano 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
6251ce50e7dSDaniel Lezcano 		.doit = thermal_genl_cmd_doit,
6261ce50e7dSDaniel Lezcano 	},
6271ce50e7dSDaniel Lezcano 	{
6281ce50e7dSDaniel Lezcano 		.cmd = THERMAL_GENL_CMD_CDEV_GET,
6291ce50e7dSDaniel Lezcano 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
6301ce50e7dSDaniel Lezcano 		.dumpit = thermal_genl_cmd_dumpit,
6311ce50e7dSDaniel Lezcano 	},
6321ce50e7dSDaniel Lezcano };
6331ce50e7dSDaniel Lezcano 
6341ce50e7dSDaniel Lezcano static struct genl_family thermal_gnl_family __ro_after_init = {
6351ce50e7dSDaniel Lezcano 	.hdrsize	= 0,
6361ce50e7dSDaniel Lezcano 	.name		= THERMAL_GENL_FAMILY_NAME,
6371ce50e7dSDaniel Lezcano 	.version	= THERMAL_GENL_VERSION,
6381ce50e7dSDaniel Lezcano 	.maxattr	= THERMAL_GENL_ATTR_MAX,
6391ce50e7dSDaniel Lezcano 	.policy		= thermal_genl_policy,
64066a9b928SJakub Kicinski 	.small_ops	= thermal_genl_ops,
64166a9b928SJakub Kicinski 	.n_small_ops	= ARRAY_SIZE(thermal_genl_ops),
6421ce50e7dSDaniel Lezcano 	.mcgrps		= thermal_genl_mcgrps,
6431ce50e7dSDaniel Lezcano 	.n_mcgrps	= ARRAY_SIZE(thermal_genl_mcgrps),
6441ce50e7dSDaniel Lezcano };
6451ce50e7dSDaniel Lezcano 
646d2a89b52SDaniel Lezcano int __init thermal_netlink_init(void)
6471ce50e7dSDaniel Lezcano {
6481ce50e7dSDaniel Lezcano 	return genl_register_family(&thermal_gnl_family);
6491ce50e7dSDaniel Lezcano }
650