xref: /openbmc/linux/drivers/thermal/thermal_netlink.c (revision 1ce50e7d408ef2bdc8ca021363fd46d1b8bfad00)
1*1ce50e7dSDaniel Lezcano // SPDX-License-Identifier: GPL-2.0
2*1ce50e7dSDaniel Lezcano /*
3*1ce50e7dSDaniel Lezcano  * Copyright 2020 Linaro Limited
4*1ce50e7dSDaniel Lezcano  *
5*1ce50e7dSDaniel Lezcano  * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
6*1ce50e7dSDaniel Lezcano  *
7*1ce50e7dSDaniel Lezcano  * Generic netlink for thermal management framework
8*1ce50e7dSDaniel Lezcano  */
9*1ce50e7dSDaniel Lezcano #include <linux/module.h>
10*1ce50e7dSDaniel Lezcano #include <linux/kernel.h>
11*1ce50e7dSDaniel Lezcano #include <net/genetlink.h>
12*1ce50e7dSDaniel Lezcano #include <uapi/linux/thermal.h>
13*1ce50e7dSDaniel Lezcano 
14*1ce50e7dSDaniel Lezcano #include "thermal_core.h"
15*1ce50e7dSDaniel Lezcano 
16*1ce50e7dSDaniel Lezcano static const struct genl_multicast_group thermal_genl_mcgrps[] = {
17*1ce50e7dSDaniel Lezcano 	{ .name = THERMAL_GENL_SAMPLING_GROUP_NAME, },
18*1ce50e7dSDaniel Lezcano 	{ .name = THERMAL_GENL_EVENT_GROUP_NAME,  },
19*1ce50e7dSDaniel Lezcano };
20*1ce50e7dSDaniel Lezcano 
21*1ce50e7dSDaniel Lezcano static const struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = {
22*1ce50e7dSDaniel Lezcano 	/* Thermal zone */
23*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ]			= { .type = NLA_NESTED },
24*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_ID]		= { .type = NLA_U32 },
25*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_TEMP]		= { .type = NLA_U32 },
26*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_TRIP]		= { .type = NLA_NESTED },
27*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_TRIP_ID]		= { .type = NLA_U32 },
28*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]	= { .type = NLA_U32 },
29*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]	= { .type = NLA_U32 },
30*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_TRIP_HYST]	= { .type = NLA_U32 },
31*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_MODE]		= { .type = NLA_U32 },
32*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT]	= { .type = NLA_U32 },
33*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_NAME]		= { .type = NLA_STRING,
34*1ce50e7dSDaniel Lezcano 						    .len = THERMAL_NAME_LENGTH },
35*1ce50e7dSDaniel Lezcano 	/* Governor(s) */
36*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_GOV]		= { .type = NLA_NESTED },
37*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_TZ_GOV_NAME]		= { .type = NLA_STRING,
38*1ce50e7dSDaniel Lezcano 						    .len = THERMAL_NAME_LENGTH },
39*1ce50e7dSDaniel Lezcano 	/* Cooling devices */
40*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_CDEV]		= { .type = NLA_NESTED },
41*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_CDEV_ID]		= { .type = NLA_U32 },
42*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_CDEV_CUR_STATE]	= { .type = NLA_U32 },
43*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_CDEV_MAX_STATE]	= { .type = NLA_U32 },
44*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_ATTR_CDEV_NAME]		= { .type = NLA_STRING,
45*1ce50e7dSDaniel Lezcano 						    .len = THERMAL_NAME_LENGTH },
46*1ce50e7dSDaniel Lezcano };
47*1ce50e7dSDaniel Lezcano 
48*1ce50e7dSDaniel Lezcano struct param {
49*1ce50e7dSDaniel Lezcano 	struct nlattr **attrs;
50*1ce50e7dSDaniel Lezcano 	struct sk_buff *msg;
51*1ce50e7dSDaniel Lezcano 	const char *name;
52*1ce50e7dSDaniel Lezcano 	int tz_id;
53*1ce50e7dSDaniel Lezcano 	int cdev_id;
54*1ce50e7dSDaniel Lezcano 	int trip_id;
55*1ce50e7dSDaniel Lezcano 	int trip_temp;
56*1ce50e7dSDaniel Lezcano 	int trip_type;
57*1ce50e7dSDaniel Lezcano 	int trip_hyst;
58*1ce50e7dSDaniel Lezcano 	int temp;
59*1ce50e7dSDaniel Lezcano 	int cdev_state;
60*1ce50e7dSDaniel Lezcano 	int cdev_max_state;
61*1ce50e7dSDaniel Lezcano };
62*1ce50e7dSDaniel Lezcano 
63*1ce50e7dSDaniel Lezcano typedef int (*cb_t)(struct param *);
64*1ce50e7dSDaniel Lezcano 
65*1ce50e7dSDaniel Lezcano static struct genl_family thermal_gnl_family;
66*1ce50e7dSDaniel Lezcano 
67*1ce50e7dSDaniel Lezcano /************************** Sampling encoding *******************************/
68*1ce50e7dSDaniel Lezcano 
69*1ce50e7dSDaniel Lezcano int thermal_genl_sampling_temp(int id, int temp)
70*1ce50e7dSDaniel Lezcano {
71*1ce50e7dSDaniel Lezcano 	struct sk_buff *skb;
72*1ce50e7dSDaniel Lezcano 	void *hdr;
73*1ce50e7dSDaniel Lezcano 
74*1ce50e7dSDaniel Lezcano 	skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
75*1ce50e7dSDaniel Lezcano 	if (!skb)
76*1ce50e7dSDaniel Lezcano 		return -ENOMEM;
77*1ce50e7dSDaniel Lezcano 
78*1ce50e7dSDaniel Lezcano 	hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0,
79*1ce50e7dSDaniel Lezcano 			  THERMAL_GENL_SAMPLING_TEMP);
80*1ce50e7dSDaniel Lezcano 	if (!hdr)
81*1ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
82*1ce50e7dSDaniel Lezcano 
83*1ce50e7dSDaniel Lezcano 	if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_ID, id))
84*1ce50e7dSDaniel Lezcano 		goto out_cancel;
85*1ce50e7dSDaniel Lezcano 
86*1ce50e7dSDaniel Lezcano 	if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_TEMP, temp))
87*1ce50e7dSDaniel Lezcano 		goto out_cancel;
88*1ce50e7dSDaniel Lezcano 
89*1ce50e7dSDaniel Lezcano 	genlmsg_end(skb, hdr);
90*1ce50e7dSDaniel Lezcano 
91*1ce50e7dSDaniel Lezcano 	genlmsg_multicast(&thermal_gnl_family, skb, 0, 0, GFP_KERNEL);
92*1ce50e7dSDaniel Lezcano 
93*1ce50e7dSDaniel Lezcano 	return 0;
94*1ce50e7dSDaniel Lezcano out_cancel:
95*1ce50e7dSDaniel Lezcano 	genlmsg_cancel(skb, hdr);
96*1ce50e7dSDaniel Lezcano 	nlmsg_free(skb);
97*1ce50e7dSDaniel Lezcano 
98*1ce50e7dSDaniel Lezcano 	return -EMSGSIZE;
99*1ce50e7dSDaniel Lezcano }
100*1ce50e7dSDaniel Lezcano 
101*1ce50e7dSDaniel Lezcano /**************************** Event encoding *********************************/
102*1ce50e7dSDaniel Lezcano 
103*1ce50e7dSDaniel Lezcano static int thermal_genl_event_tz_create(struct param *p)
104*1ce50e7dSDaniel Lezcano {
105*1ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
106*1ce50e7dSDaniel Lezcano 	    nla_put_string(p->msg, THERMAL_GENL_ATTR_TZ_NAME, p->name))
107*1ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
108*1ce50e7dSDaniel Lezcano 
109*1ce50e7dSDaniel Lezcano 	return 0;
110*1ce50e7dSDaniel Lezcano }
111*1ce50e7dSDaniel Lezcano 
112*1ce50e7dSDaniel Lezcano static int thermal_genl_event_tz(struct param *p)
113*1ce50e7dSDaniel Lezcano {
114*1ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id))
115*1ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
116*1ce50e7dSDaniel Lezcano 
117*1ce50e7dSDaniel Lezcano 	return 0;
118*1ce50e7dSDaniel Lezcano }
119*1ce50e7dSDaniel Lezcano 
120*1ce50e7dSDaniel Lezcano static int thermal_genl_event_tz_trip_up(struct param *p)
121*1ce50e7dSDaniel Lezcano {
122*1ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
123*1ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id))
124*1ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
125*1ce50e7dSDaniel Lezcano 
126*1ce50e7dSDaniel Lezcano 	return 0;
127*1ce50e7dSDaniel Lezcano }
128*1ce50e7dSDaniel Lezcano 
129*1ce50e7dSDaniel Lezcano static int thermal_genl_event_tz_trip_add(struct param *p)
130*1ce50e7dSDaniel Lezcano {
131*1ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
132*1ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id) ||
133*1ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, p->trip_type) ||
134*1ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, p->trip_temp) ||
135*1ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, p->trip_hyst))
136*1ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
137*1ce50e7dSDaniel Lezcano 
138*1ce50e7dSDaniel Lezcano 	return 0;
139*1ce50e7dSDaniel Lezcano }
140*1ce50e7dSDaniel Lezcano 
141*1ce50e7dSDaniel Lezcano static int thermal_genl_event_tz_trip_delete(struct param *p)
142*1ce50e7dSDaniel Lezcano {
143*1ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
144*1ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id))
145*1ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
146*1ce50e7dSDaniel Lezcano 
147*1ce50e7dSDaniel Lezcano 	return 0;
148*1ce50e7dSDaniel Lezcano }
149*1ce50e7dSDaniel Lezcano 
150*1ce50e7dSDaniel Lezcano static int thermal_genl_event_cdev_add(struct param *p)
151*1ce50e7dSDaniel Lezcano {
152*1ce50e7dSDaniel Lezcano 	if (nla_put_string(p->msg, THERMAL_GENL_ATTR_CDEV_NAME,
153*1ce50e7dSDaniel Lezcano 			   p->name) ||
154*1ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID,
155*1ce50e7dSDaniel Lezcano 			p->cdev_id) ||
156*1ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_MAX_STATE,
157*1ce50e7dSDaniel Lezcano 			p->cdev_max_state))
158*1ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
159*1ce50e7dSDaniel Lezcano 
160*1ce50e7dSDaniel Lezcano 	return 0;
161*1ce50e7dSDaniel Lezcano }
162*1ce50e7dSDaniel Lezcano 
163*1ce50e7dSDaniel Lezcano static int thermal_genl_event_cdev_delete(struct param *p)
164*1ce50e7dSDaniel Lezcano {
165*1ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID, p->cdev_id))
166*1ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
167*1ce50e7dSDaniel Lezcano 
168*1ce50e7dSDaniel Lezcano 	return 0;
169*1ce50e7dSDaniel Lezcano }
170*1ce50e7dSDaniel Lezcano 
171*1ce50e7dSDaniel Lezcano static int thermal_genl_event_cdev_state_update(struct param *p)
172*1ce50e7dSDaniel Lezcano {
173*1ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID,
174*1ce50e7dSDaniel Lezcano 			p->cdev_id) ||
175*1ce50e7dSDaniel Lezcano 	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_CUR_STATE,
176*1ce50e7dSDaniel Lezcano 			p->cdev_state))
177*1ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
178*1ce50e7dSDaniel Lezcano 
179*1ce50e7dSDaniel Lezcano 	return 0;
180*1ce50e7dSDaniel Lezcano }
181*1ce50e7dSDaniel Lezcano 
182*1ce50e7dSDaniel Lezcano static int thermal_genl_event_gov_change(struct param *p)
183*1ce50e7dSDaniel Lezcano {
184*1ce50e7dSDaniel Lezcano 	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
185*1ce50e7dSDaniel Lezcano 	    nla_put_string(p->msg, THERMAL_GENL_ATTR_GOV_NAME, p->name))
186*1ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
187*1ce50e7dSDaniel Lezcano 
188*1ce50e7dSDaniel Lezcano 	return 0;
189*1ce50e7dSDaniel Lezcano }
190*1ce50e7dSDaniel Lezcano 
191*1ce50e7dSDaniel Lezcano int thermal_genl_event_tz_delete(struct param *p)
192*1ce50e7dSDaniel Lezcano 	__attribute__((alias("thermal_genl_event_tz")));
193*1ce50e7dSDaniel Lezcano 
194*1ce50e7dSDaniel Lezcano int thermal_genl_event_tz_enable(struct param *p)
195*1ce50e7dSDaniel Lezcano 	__attribute__((alias("thermal_genl_event_tz")));
196*1ce50e7dSDaniel Lezcano 
197*1ce50e7dSDaniel Lezcano int thermal_genl_event_tz_disable(struct param *p)
198*1ce50e7dSDaniel Lezcano 	__attribute__((alias("thermal_genl_event_tz")));
199*1ce50e7dSDaniel Lezcano 
200*1ce50e7dSDaniel Lezcano int thermal_genl_event_tz_trip_down(struct param *p)
201*1ce50e7dSDaniel Lezcano 	__attribute__((alias("thermal_genl_event_tz_trip_up")));
202*1ce50e7dSDaniel Lezcano 
203*1ce50e7dSDaniel Lezcano int thermal_genl_event_tz_trip_change(struct param *p)
204*1ce50e7dSDaniel Lezcano 	__attribute__((alias("thermal_genl_event_tz_trip_add")));
205*1ce50e7dSDaniel Lezcano 
206*1ce50e7dSDaniel Lezcano static cb_t event_cb[] = {
207*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_CREATE]		= thermal_genl_event_tz_create,
208*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_DELETE]		= thermal_genl_event_tz_delete,
209*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_ENABLE]		= thermal_genl_event_tz_enable,
210*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_DISABLE]		= thermal_genl_event_tz_disable,
211*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_TRIP_UP]		= thermal_genl_event_tz_trip_up,
212*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_TRIP_DOWN]	= thermal_genl_event_tz_trip_down,
213*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_TRIP_CHANGE]	= thermal_genl_event_tz_trip_change,
214*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_TRIP_ADD]	= thermal_genl_event_tz_trip_add,
215*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_TRIP_DELETE]	= thermal_genl_event_tz_trip_delete,
216*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_CDEV_ADD]		= thermal_genl_event_cdev_add,
217*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_CDEV_DELETE]	= thermal_genl_event_cdev_delete,
218*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE]	= thermal_genl_event_cdev_state_update,
219*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_EVENT_TZ_GOV_CHANGE]	= thermal_genl_event_gov_change,
220*1ce50e7dSDaniel Lezcano };
221*1ce50e7dSDaniel Lezcano 
222*1ce50e7dSDaniel Lezcano /*
223*1ce50e7dSDaniel Lezcano  * Generic netlink event encoding
224*1ce50e7dSDaniel Lezcano  */
225*1ce50e7dSDaniel Lezcano static int thermal_genl_send_event(enum thermal_genl_event event,
226*1ce50e7dSDaniel Lezcano 				   struct param *p)
227*1ce50e7dSDaniel Lezcano {
228*1ce50e7dSDaniel Lezcano 	struct sk_buff *msg;
229*1ce50e7dSDaniel Lezcano 	int ret = -EMSGSIZE;
230*1ce50e7dSDaniel Lezcano 	void *hdr;
231*1ce50e7dSDaniel Lezcano 
232*1ce50e7dSDaniel Lezcano 	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
233*1ce50e7dSDaniel Lezcano 	if (!msg)
234*1ce50e7dSDaniel Lezcano 		return -ENOMEM;
235*1ce50e7dSDaniel Lezcano 	p->msg = msg;
236*1ce50e7dSDaniel Lezcano 
237*1ce50e7dSDaniel Lezcano 	hdr = genlmsg_put(msg, 0, 0, &thermal_gnl_family, 0, event);
238*1ce50e7dSDaniel Lezcano 	if (!hdr)
239*1ce50e7dSDaniel Lezcano 		goto out_free_msg;
240*1ce50e7dSDaniel Lezcano 
241*1ce50e7dSDaniel Lezcano 	ret = event_cb[event](p);
242*1ce50e7dSDaniel Lezcano 	if (ret)
243*1ce50e7dSDaniel Lezcano 		goto out_cancel_msg;
244*1ce50e7dSDaniel Lezcano 
245*1ce50e7dSDaniel Lezcano 	genlmsg_end(msg, hdr);
246*1ce50e7dSDaniel Lezcano 
247*1ce50e7dSDaniel Lezcano 	genlmsg_multicast(&thermal_gnl_family, msg, 0, 1, GFP_KERNEL);
248*1ce50e7dSDaniel Lezcano 
249*1ce50e7dSDaniel Lezcano 	return 0;
250*1ce50e7dSDaniel Lezcano 
251*1ce50e7dSDaniel Lezcano out_cancel_msg:
252*1ce50e7dSDaniel Lezcano 	genlmsg_cancel(msg, hdr);
253*1ce50e7dSDaniel Lezcano out_free_msg:
254*1ce50e7dSDaniel Lezcano 	nlmsg_free(msg);
255*1ce50e7dSDaniel Lezcano 
256*1ce50e7dSDaniel Lezcano 	return ret;
257*1ce50e7dSDaniel Lezcano }
258*1ce50e7dSDaniel Lezcano 
259*1ce50e7dSDaniel Lezcano int thermal_notify_tz_create(int tz_id, const char *name)
260*1ce50e7dSDaniel Lezcano {
261*1ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id, .name = name };
262*1ce50e7dSDaniel Lezcano 
263*1ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_CREATE, &p);
264*1ce50e7dSDaniel Lezcano }
265*1ce50e7dSDaniel Lezcano 
266*1ce50e7dSDaniel Lezcano int thermal_notify_tz_delete(int tz_id)
267*1ce50e7dSDaniel Lezcano {
268*1ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id };
269*1ce50e7dSDaniel Lezcano 
270*1ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DELETE, &p);
271*1ce50e7dSDaniel Lezcano }
272*1ce50e7dSDaniel Lezcano 
273*1ce50e7dSDaniel Lezcano int thermal_notify_tz_enable(int tz_id)
274*1ce50e7dSDaniel Lezcano {
275*1ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id };
276*1ce50e7dSDaniel Lezcano 
277*1ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_ENABLE, &p);
278*1ce50e7dSDaniel Lezcano }
279*1ce50e7dSDaniel Lezcano 
280*1ce50e7dSDaniel Lezcano int thermal_notify_tz_disable(int tz_id)
281*1ce50e7dSDaniel Lezcano {
282*1ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id };
283*1ce50e7dSDaniel Lezcano 
284*1ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DISABLE, &p);
285*1ce50e7dSDaniel Lezcano }
286*1ce50e7dSDaniel Lezcano 
287*1ce50e7dSDaniel Lezcano int thermal_notify_tz_trip_down(int tz_id, int trip_id)
288*1ce50e7dSDaniel Lezcano {
289*1ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id, .trip_id = trip_id };
290*1ce50e7dSDaniel Lezcano 
291*1ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DOWN, &p);
292*1ce50e7dSDaniel Lezcano }
293*1ce50e7dSDaniel Lezcano 
294*1ce50e7dSDaniel Lezcano int thermal_notify_tz_trip_up(int tz_id, int trip_id)
295*1ce50e7dSDaniel Lezcano {
296*1ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id, .trip_id = trip_id };
297*1ce50e7dSDaniel Lezcano 
298*1ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_UP, &p);
299*1ce50e7dSDaniel Lezcano }
300*1ce50e7dSDaniel Lezcano 
301*1ce50e7dSDaniel Lezcano int thermal_notify_tz_trip_add(int tz_id, int trip_id, int trip_type,
302*1ce50e7dSDaniel Lezcano 			       int trip_temp, int trip_hyst)
303*1ce50e7dSDaniel Lezcano {
304*1ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id, .trip_id = trip_id,
305*1ce50e7dSDaniel Lezcano 			   .trip_type = trip_type, .trip_temp = trip_temp,
306*1ce50e7dSDaniel Lezcano 			   .trip_hyst = trip_hyst };
307*1ce50e7dSDaniel Lezcano 
308*1ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_ADD, &p);
309*1ce50e7dSDaniel Lezcano }
310*1ce50e7dSDaniel Lezcano 
311*1ce50e7dSDaniel Lezcano int thermal_notify_tz_trip_delete(int tz_id, int trip_id)
312*1ce50e7dSDaniel Lezcano {
313*1ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id, .trip_id = trip_id };
314*1ce50e7dSDaniel Lezcano 
315*1ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DELETE, &p);
316*1ce50e7dSDaniel Lezcano }
317*1ce50e7dSDaniel Lezcano 
318*1ce50e7dSDaniel Lezcano int thermal_notify_tz_trip_change(int tz_id, int trip_id, int trip_type,
319*1ce50e7dSDaniel Lezcano 				  int trip_temp, int trip_hyst)
320*1ce50e7dSDaniel Lezcano {
321*1ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id, .trip_id = trip_id,
322*1ce50e7dSDaniel Lezcano 			   .trip_type = trip_type, .trip_temp = trip_temp,
323*1ce50e7dSDaniel Lezcano 			   .trip_hyst = trip_hyst };
324*1ce50e7dSDaniel Lezcano 
325*1ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_CHANGE, &p);
326*1ce50e7dSDaniel Lezcano }
327*1ce50e7dSDaniel Lezcano 
328*1ce50e7dSDaniel Lezcano int thermal_notify_cdev_state_update(int cdev_id, int cdev_state)
329*1ce50e7dSDaniel Lezcano {
330*1ce50e7dSDaniel Lezcano 	struct param p = { .cdev_id = cdev_id, .cdev_state = cdev_state };
331*1ce50e7dSDaniel Lezcano 
332*1ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, &p);
333*1ce50e7dSDaniel Lezcano }
334*1ce50e7dSDaniel Lezcano 
335*1ce50e7dSDaniel Lezcano int thermal_notify_cdev_add(int cdev_id, const char *name, int cdev_max_state)
336*1ce50e7dSDaniel Lezcano {
337*1ce50e7dSDaniel Lezcano 	struct param p = { .cdev_id = cdev_id, .name = name,
338*1ce50e7dSDaniel Lezcano 			   .cdev_max_state = cdev_max_state };
339*1ce50e7dSDaniel Lezcano 
340*1ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_ADD, &p);
341*1ce50e7dSDaniel Lezcano }
342*1ce50e7dSDaniel Lezcano 
343*1ce50e7dSDaniel Lezcano int thermal_notify_cdev_delete(int cdev_id)
344*1ce50e7dSDaniel Lezcano {
345*1ce50e7dSDaniel Lezcano 	struct param p = { .cdev_id = cdev_id };
346*1ce50e7dSDaniel Lezcano 
347*1ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_DELETE, &p);
348*1ce50e7dSDaniel Lezcano }
349*1ce50e7dSDaniel Lezcano 
350*1ce50e7dSDaniel Lezcano int thermal_notify_tz_gov_change(int tz_id, const char *name)
351*1ce50e7dSDaniel Lezcano {
352*1ce50e7dSDaniel Lezcano 	struct param p = { .tz_id = tz_id, .name = name };
353*1ce50e7dSDaniel Lezcano 
354*1ce50e7dSDaniel Lezcano 	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_GOV_CHANGE, &p);
355*1ce50e7dSDaniel Lezcano }
356*1ce50e7dSDaniel Lezcano 
357*1ce50e7dSDaniel Lezcano /*************************** Command encoding ********************************/
358*1ce50e7dSDaniel Lezcano 
359*1ce50e7dSDaniel Lezcano static int __thermal_genl_cmd_tz_get_id(struct thermal_zone_device *tz,
360*1ce50e7dSDaniel Lezcano 					void *data)
361*1ce50e7dSDaniel Lezcano {
362*1ce50e7dSDaniel Lezcano 	struct sk_buff *msg = data;
363*1ce50e7dSDaniel Lezcano 
364*1ce50e7dSDaniel Lezcano 	if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, tz->id) ||
365*1ce50e7dSDaniel Lezcano 	    nla_put_string(msg, THERMAL_GENL_ATTR_TZ_NAME, tz->type))
366*1ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
367*1ce50e7dSDaniel Lezcano 
368*1ce50e7dSDaniel Lezcano 	return 0;
369*1ce50e7dSDaniel Lezcano }
370*1ce50e7dSDaniel Lezcano 
371*1ce50e7dSDaniel Lezcano static int thermal_genl_cmd_tz_get_id(struct param *p)
372*1ce50e7dSDaniel Lezcano {
373*1ce50e7dSDaniel Lezcano 	struct sk_buff *msg = p->msg;
374*1ce50e7dSDaniel Lezcano 	struct nlattr *start_tz;
375*1ce50e7dSDaniel Lezcano 	int ret;
376*1ce50e7dSDaniel Lezcano 
377*1ce50e7dSDaniel Lezcano 	start_tz = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ);
378*1ce50e7dSDaniel Lezcano 	if (!start_tz)
379*1ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
380*1ce50e7dSDaniel Lezcano 
381*1ce50e7dSDaniel Lezcano 	ret = for_each_thermal_zone(__thermal_genl_cmd_tz_get_id, msg);
382*1ce50e7dSDaniel Lezcano 	if (ret)
383*1ce50e7dSDaniel Lezcano 		goto out_cancel_nest;
384*1ce50e7dSDaniel Lezcano 
385*1ce50e7dSDaniel Lezcano 	nla_nest_end(msg, start_tz);
386*1ce50e7dSDaniel Lezcano 
387*1ce50e7dSDaniel Lezcano 	return 0;
388*1ce50e7dSDaniel Lezcano 
389*1ce50e7dSDaniel Lezcano out_cancel_nest:
390*1ce50e7dSDaniel Lezcano 	nla_nest_cancel(msg, start_tz);
391*1ce50e7dSDaniel Lezcano 
392*1ce50e7dSDaniel Lezcano 	return ret;
393*1ce50e7dSDaniel Lezcano }
394*1ce50e7dSDaniel Lezcano 
395*1ce50e7dSDaniel Lezcano static int thermal_genl_cmd_tz_get_trip(struct param *p)
396*1ce50e7dSDaniel Lezcano {
397*1ce50e7dSDaniel Lezcano 	struct sk_buff *msg = p->msg;
398*1ce50e7dSDaniel Lezcano 	struct thermal_zone_device *tz;
399*1ce50e7dSDaniel Lezcano 	struct nlattr *start_trip;
400*1ce50e7dSDaniel Lezcano 	int i, id;
401*1ce50e7dSDaniel Lezcano 
402*1ce50e7dSDaniel Lezcano 	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
403*1ce50e7dSDaniel Lezcano 		return -EINVAL;
404*1ce50e7dSDaniel Lezcano 
405*1ce50e7dSDaniel Lezcano 	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
406*1ce50e7dSDaniel Lezcano 
407*1ce50e7dSDaniel Lezcano 	tz = thermal_zone_get_by_id(id);
408*1ce50e7dSDaniel Lezcano 	if (!tz)
409*1ce50e7dSDaniel Lezcano 		return -EINVAL;
410*1ce50e7dSDaniel Lezcano 
411*1ce50e7dSDaniel Lezcano 	start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ_TRIP);
412*1ce50e7dSDaniel Lezcano 	if (!start_trip)
413*1ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
414*1ce50e7dSDaniel Lezcano 
415*1ce50e7dSDaniel Lezcano 	mutex_lock(&tz->lock);
416*1ce50e7dSDaniel Lezcano 
417*1ce50e7dSDaniel Lezcano 	for (i = 0; i < tz->trips; i++) {
418*1ce50e7dSDaniel Lezcano 
419*1ce50e7dSDaniel Lezcano 		enum thermal_trip_type type;
420*1ce50e7dSDaniel Lezcano 		int temp, hyst;
421*1ce50e7dSDaniel Lezcano 
422*1ce50e7dSDaniel Lezcano 		tz->ops->get_trip_type(tz, i, &type);
423*1ce50e7dSDaniel Lezcano 		tz->ops->get_trip_temp(tz, i, &temp);
424*1ce50e7dSDaniel Lezcano 		tz->ops->get_trip_hyst(tz, i, &hyst);
425*1ce50e7dSDaniel Lezcano 
426*1ce50e7dSDaniel Lezcano 		if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, i) ||
427*1ce50e7dSDaniel Lezcano 		    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, type) ||
428*1ce50e7dSDaniel Lezcano 		    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, temp) ||
429*1ce50e7dSDaniel Lezcano 		    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, hyst))
430*1ce50e7dSDaniel Lezcano 			goto out_cancel_nest;
431*1ce50e7dSDaniel Lezcano 	}
432*1ce50e7dSDaniel Lezcano 
433*1ce50e7dSDaniel Lezcano 	mutex_unlock(&tz->lock);
434*1ce50e7dSDaniel Lezcano 
435*1ce50e7dSDaniel Lezcano 	nla_nest_end(msg, start_trip);
436*1ce50e7dSDaniel Lezcano 
437*1ce50e7dSDaniel Lezcano 	return 0;
438*1ce50e7dSDaniel Lezcano 
439*1ce50e7dSDaniel Lezcano out_cancel_nest:
440*1ce50e7dSDaniel Lezcano 	mutex_unlock(&tz->lock);
441*1ce50e7dSDaniel Lezcano 
442*1ce50e7dSDaniel Lezcano 	return -EMSGSIZE;
443*1ce50e7dSDaniel Lezcano }
444*1ce50e7dSDaniel Lezcano 
445*1ce50e7dSDaniel Lezcano static int thermal_genl_cmd_tz_get_temp(struct param *p)
446*1ce50e7dSDaniel Lezcano {
447*1ce50e7dSDaniel Lezcano 	struct sk_buff *msg = p->msg;
448*1ce50e7dSDaniel Lezcano 	struct thermal_zone_device *tz;
449*1ce50e7dSDaniel Lezcano 	int temp, ret, id;
450*1ce50e7dSDaniel Lezcano 
451*1ce50e7dSDaniel Lezcano 	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
452*1ce50e7dSDaniel Lezcano 		return -EINVAL;
453*1ce50e7dSDaniel Lezcano 
454*1ce50e7dSDaniel Lezcano 	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
455*1ce50e7dSDaniel Lezcano 
456*1ce50e7dSDaniel Lezcano 	tz = thermal_zone_get_by_id(id);
457*1ce50e7dSDaniel Lezcano 	if (!tz)
458*1ce50e7dSDaniel Lezcano 		return -EINVAL;
459*1ce50e7dSDaniel Lezcano 
460*1ce50e7dSDaniel Lezcano 	ret = thermal_zone_get_temp(tz, &temp);
461*1ce50e7dSDaniel Lezcano 	if (ret)
462*1ce50e7dSDaniel Lezcano 		return ret;
463*1ce50e7dSDaniel Lezcano 
464*1ce50e7dSDaniel Lezcano 	if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
465*1ce50e7dSDaniel Lezcano 	    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TEMP, temp))
466*1ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
467*1ce50e7dSDaniel Lezcano 
468*1ce50e7dSDaniel Lezcano 	return 0;
469*1ce50e7dSDaniel Lezcano }
470*1ce50e7dSDaniel Lezcano 
471*1ce50e7dSDaniel Lezcano static int thermal_genl_cmd_tz_get_gov(struct param *p)
472*1ce50e7dSDaniel Lezcano {
473*1ce50e7dSDaniel Lezcano 	struct sk_buff *msg = p->msg;
474*1ce50e7dSDaniel Lezcano 	struct thermal_zone_device *tz;
475*1ce50e7dSDaniel Lezcano 	int id, ret = 0;
476*1ce50e7dSDaniel Lezcano 
477*1ce50e7dSDaniel Lezcano 	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
478*1ce50e7dSDaniel Lezcano 		return -EINVAL;
479*1ce50e7dSDaniel Lezcano 
480*1ce50e7dSDaniel Lezcano 	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
481*1ce50e7dSDaniel Lezcano 
482*1ce50e7dSDaniel Lezcano 	tz = thermal_zone_get_by_id(id);
483*1ce50e7dSDaniel Lezcano 	if (!tz)
484*1ce50e7dSDaniel Lezcano 		return -EINVAL;
485*1ce50e7dSDaniel Lezcano 
486*1ce50e7dSDaniel Lezcano 	mutex_lock(&tz->lock);
487*1ce50e7dSDaniel Lezcano 
488*1ce50e7dSDaniel Lezcano 	if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
489*1ce50e7dSDaniel Lezcano 	    nla_put_string(msg, THERMAL_GENL_ATTR_TZ_GOV_NAME,
490*1ce50e7dSDaniel Lezcano 			   tz->governor->name))
491*1ce50e7dSDaniel Lezcano 		ret = -EMSGSIZE;
492*1ce50e7dSDaniel Lezcano 
493*1ce50e7dSDaniel Lezcano 	mutex_unlock(&tz->lock);
494*1ce50e7dSDaniel Lezcano 
495*1ce50e7dSDaniel Lezcano 	return ret;
496*1ce50e7dSDaniel Lezcano }
497*1ce50e7dSDaniel Lezcano 
498*1ce50e7dSDaniel Lezcano static int __thermal_genl_cmd_cdev_get(struct thermal_cooling_device *cdev,
499*1ce50e7dSDaniel Lezcano 				       void *data)
500*1ce50e7dSDaniel Lezcano {
501*1ce50e7dSDaniel Lezcano 	struct sk_buff *msg = data;
502*1ce50e7dSDaniel Lezcano 
503*1ce50e7dSDaniel Lezcano 	if (nla_put_u32(msg, THERMAL_GENL_ATTR_CDEV_ID, cdev->id))
504*1ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
505*1ce50e7dSDaniel Lezcano 
506*1ce50e7dSDaniel Lezcano 	if (nla_put_string(msg, THERMAL_GENL_ATTR_CDEV_NAME, cdev->type))
507*1ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
508*1ce50e7dSDaniel Lezcano 
509*1ce50e7dSDaniel Lezcano 	return 0;
510*1ce50e7dSDaniel Lezcano }
511*1ce50e7dSDaniel Lezcano 
512*1ce50e7dSDaniel Lezcano static int thermal_genl_cmd_cdev_get(struct param *p)
513*1ce50e7dSDaniel Lezcano {
514*1ce50e7dSDaniel Lezcano 	struct sk_buff *msg = p->msg;
515*1ce50e7dSDaniel Lezcano 	struct nlattr *start_cdev;
516*1ce50e7dSDaniel Lezcano 	int ret;
517*1ce50e7dSDaniel Lezcano 
518*1ce50e7dSDaniel Lezcano 	start_cdev = nla_nest_start(msg, THERMAL_GENL_ATTR_CDEV);
519*1ce50e7dSDaniel Lezcano 	if (!start_cdev)
520*1ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
521*1ce50e7dSDaniel Lezcano 
522*1ce50e7dSDaniel Lezcano 	ret = for_each_thermal_cooling_device(__thermal_genl_cmd_cdev_get, msg);
523*1ce50e7dSDaniel Lezcano 	if (ret)
524*1ce50e7dSDaniel Lezcano 		goto out_cancel_nest;
525*1ce50e7dSDaniel Lezcano 
526*1ce50e7dSDaniel Lezcano 	nla_nest_end(msg, start_cdev);
527*1ce50e7dSDaniel Lezcano 
528*1ce50e7dSDaniel Lezcano 	return 0;
529*1ce50e7dSDaniel Lezcano out_cancel_nest:
530*1ce50e7dSDaniel Lezcano 	nla_nest_cancel(msg, start_cdev);
531*1ce50e7dSDaniel Lezcano 
532*1ce50e7dSDaniel Lezcano 	return ret;
533*1ce50e7dSDaniel Lezcano }
534*1ce50e7dSDaniel Lezcano 
535*1ce50e7dSDaniel Lezcano static cb_t cmd_cb[] = {
536*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_CMD_TZ_GET_ID]	= thermal_genl_cmd_tz_get_id,
537*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_CMD_TZ_GET_TRIP]	= thermal_genl_cmd_tz_get_trip,
538*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_CMD_TZ_GET_TEMP]	= thermal_genl_cmd_tz_get_temp,
539*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_CMD_TZ_GET_GOV]	= thermal_genl_cmd_tz_get_gov,
540*1ce50e7dSDaniel Lezcano 	[THERMAL_GENL_CMD_CDEV_GET]	= thermal_genl_cmd_cdev_get,
541*1ce50e7dSDaniel Lezcano };
542*1ce50e7dSDaniel Lezcano 
543*1ce50e7dSDaniel Lezcano static int thermal_genl_cmd_dumpit(struct sk_buff *skb,
544*1ce50e7dSDaniel Lezcano 				   struct netlink_callback *cb)
545*1ce50e7dSDaniel Lezcano {
546*1ce50e7dSDaniel Lezcano 	struct param p = { .msg = skb };
547*1ce50e7dSDaniel Lezcano 	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
548*1ce50e7dSDaniel Lezcano 	int cmd = info->ops->cmd;
549*1ce50e7dSDaniel Lezcano 	int ret = -EMSGSIZE;
550*1ce50e7dSDaniel Lezcano 	void *hdr;
551*1ce50e7dSDaniel Lezcano 
552*1ce50e7dSDaniel Lezcano 	hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0, cmd);
553*1ce50e7dSDaniel Lezcano 	if (!hdr)
554*1ce50e7dSDaniel Lezcano 		return -EMSGSIZE;
555*1ce50e7dSDaniel Lezcano 
556*1ce50e7dSDaniel Lezcano 	ret = cmd_cb[cmd](&p);
557*1ce50e7dSDaniel Lezcano 	if (ret)
558*1ce50e7dSDaniel Lezcano 		goto out_cancel_msg;
559*1ce50e7dSDaniel Lezcano 
560*1ce50e7dSDaniel Lezcano 	genlmsg_end(skb, hdr);
561*1ce50e7dSDaniel Lezcano 
562*1ce50e7dSDaniel Lezcano 	return 0;
563*1ce50e7dSDaniel Lezcano 
564*1ce50e7dSDaniel Lezcano out_cancel_msg:
565*1ce50e7dSDaniel Lezcano 	genlmsg_cancel(skb, hdr);
566*1ce50e7dSDaniel Lezcano 
567*1ce50e7dSDaniel Lezcano 	return ret;
568*1ce50e7dSDaniel Lezcano }
569*1ce50e7dSDaniel Lezcano 
570*1ce50e7dSDaniel Lezcano static int thermal_genl_cmd_doit(struct sk_buff *skb,
571*1ce50e7dSDaniel Lezcano 				 struct genl_info *info)
572*1ce50e7dSDaniel Lezcano {
573*1ce50e7dSDaniel Lezcano 	struct param p = { .attrs = info->attrs };
574*1ce50e7dSDaniel Lezcano 	struct sk_buff *msg;
575*1ce50e7dSDaniel Lezcano 	void *hdr;
576*1ce50e7dSDaniel Lezcano 	int cmd = info->genlhdr->cmd;
577*1ce50e7dSDaniel Lezcano 	int ret = -EMSGSIZE;
578*1ce50e7dSDaniel Lezcano 
579*1ce50e7dSDaniel Lezcano 	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
580*1ce50e7dSDaniel Lezcano 	if (!msg)
581*1ce50e7dSDaniel Lezcano 		return -ENOMEM;
582*1ce50e7dSDaniel Lezcano 	p.msg = msg;
583*1ce50e7dSDaniel Lezcano 
584*1ce50e7dSDaniel Lezcano 	hdr = genlmsg_put_reply(msg, info, &thermal_gnl_family, 0, cmd);
585*1ce50e7dSDaniel Lezcano 	if (!hdr)
586*1ce50e7dSDaniel Lezcano 		goto out_free_msg;
587*1ce50e7dSDaniel Lezcano 
588*1ce50e7dSDaniel Lezcano 	ret = cmd_cb[cmd](&p);
589*1ce50e7dSDaniel Lezcano 	if (ret)
590*1ce50e7dSDaniel Lezcano 		goto out_cancel_msg;
591*1ce50e7dSDaniel Lezcano 
592*1ce50e7dSDaniel Lezcano 	genlmsg_end(msg, hdr);
593*1ce50e7dSDaniel Lezcano 
594*1ce50e7dSDaniel Lezcano 	return genlmsg_reply(msg, info);
595*1ce50e7dSDaniel Lezcano 
596*1ce50e7dSDaniel Lezcano out_cancel_msg:
597*1ce50e7dSDaniel Lezcano 	genlmsg_cancel(msg, hdr);
598*1ce50e7dSDaniel Lezcano out_free_msg:
599*1ce50e7dSDaniel Lezcano 	nlmsg_free(msg);
600*1ce50e7dSDaniel Lezcano 
601*1ce50e7dSDaniel Lezcano 	return ret;
602*1ce50e7dSDaniel Lezcano }
603*1ce50e7dSDaniel Lezcano 
604*1ce50e7dSDaniel Lezcano static const struct genl_ops thermal_genl_ops[] = {
605*1ce50e7dSDaniel Lezcano 	{
606*1ce50e7dSDaniel Lezcano 		.cmd = THERMAL_GENL_CMD_TZ_GET_ID,
607*1ce50e7dSDaniel Lezcano 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
608*1ce50e7dSDaniel Lezcano 		.dumpit = thermal_genl_cmd_dumpit,
609*1ce50e7dSDaniel Lezcano 	},
610*1ce50e7dSDaniel Lezcano 	{
611*1ce50e7dSDaniel Lezcano 		.cmd = THERMAL_GENL_CMD_TZ_GET_TRIP,
612*1ce50e7dSDaniel Lezcano 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
613*1ce50e7dSDaniel Lezcano 		.doit = thermal_genl_cmd_doit,
614*1ce50e7dSDaniel Lezcano 	},
615*1ce50e7dSDaniel Lezcano 	{
616*1ce50e7dSDaniel Lezcano 		.cmd = THERMAL_GENL_CMD_TZ_GET_TEMP,
617*1ce50e7dSDaniel Lezcano 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
618*1ce50e7dSDaniel Lezcano 		.doit = thermal_genl_cmd_doit,
619*1ce50e7dSDaniel Lezcano 	},
620*1ce50e7dSDaniel Lezcano 	{
621*1ce50e7dSDaniel Lezcano 		.cmd = THERMAL_GENL_CMD_TZ_GET_GOV,
622*1ce50e7dSDaniel Lezcano 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
623*1ce50e7dSDaniel Lezcano 		.doit = thermal_genl_cmd_doit,
624*1ce50e7dSDaniel Lezcano 	},
625*1ce50e7dSDaniel Lezcano 	{
626*1ce50e7dSDaniel Lezcano 		.cmd = THERMAL_GENL_CMD_CDEV_GET,
627*1ce50e7dSDaniel Lezcano 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
628*1ce50e7dSDaniel Lezcano 		.dumpit = thermal_genl_cmd_dumpit,
629*1ce50e7dSDaniel Lezcano 	},
630*1ce50e7dSDaniel Lezcano };
631*1ce50e7dSDaniel Lezcano 
632*1ce50e7dSDaniel Lezcano static struct genl_family thermal_gnl_family __ro_after_init = {
633*1ce50e7dSDaniel Lezcano 	.hdrsize	= 0,
634*1ce50e7dSDaniel Lezcano 	.name		= THERMAL_GENL_FAMILY_NAME,
635*1ce50e7dSDaniel Lezcano 	.version	= THERMAL_GENL_VERSION,
636*1ce50e7dSDaniel Lezcano 	.maxattr	= THERMAL_GENL_ATTR_MAX,
637*1ce50e7dSDaniel Lezcano 	.policy		= thermal_genl_policy,
638*1ce50e7dSDaniel Lezcano 	.ops		= thermal_genl_ops,
639*1ce50e7dSDaniel Lezcano 	.n_ops		= ARRAY_SIZE(thermal_genl_ops),
640*1ce50e7dSDaniel Lezcano 	.mcgrps		= thermal_genl_mcgrps,
641*1ce50e7dSDaniel Lezcano 	.n_mcgrps	= ARRAY_SIZE(thermal_genl_mcgrps),
642*1ce50e7dSDaniel Lezcano };
643*1ce50e7dSDaniel Lezcano 
644*1ce50e7dSDaniel Lezcano static int __init thermal_netlink_init(void)
645*1ce50e7dSDaniel Lezcano {
646*1ce50e7dSDaniel Lezcano 	return genl_register_family(&thermal_gnl_family);
647*1ce50e7dSDaniel Lezcano }
648*1ce50e7dSDaniel Lezcano core_initcall(thermal_netlink_init);
649