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