Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  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
 16static const struct genl_multicast_group thermal_genl_mcgrps[] = {
 17	{ .name = THERMAL_GENL_SAMPLING_GROUP_NAME, },
 18	{ .name = THERMAL_GENL_EVENT_GROUP_NAME,  },
 19};
 20
 21static 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
 48struct 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
 63typedef int (*cb_t)(struct param *);
 64
 65static struct genl_family thermal_gnl_family;
 66
 67/************************** Sampling encoding *******************************/
 68
 69int 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;
 94out_cancel:
 95	genlmsg_cancel(skb, hdr);
 96	nlmsg_free(skb);
 97
 98	return -EMSGSIZE;
 99}
100
101/**************************** Event encoding *********************************/
102
103static 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
112static 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
120static 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
129static 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
141static 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
150static 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
163static 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
171static 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
182static 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
191int thermal_genl_event_tz_delete(struct param *p)
192	__attribute__((alias("thermal_genl_event_tz")));
193
194int thermal_genl_event_tz_enable(struct param *p)
195	__attribute__((alias("thermal_genl_event_tz")));
196
197int thermal_genl_event_tz_disable(struct param *p)
198	__attribute__((alias("thermal_genl_event_tz")));
199
200int thermal_genl_event_tz_trip_down(struct param *p)
201	__attribute__((alias("thermal_genl_event_tz_trip_up")));
202
203int thermal_genl_event_tz_trip_change(struct param *p)
204	__attribute__((alias("thermal_genl_event_tz_trip_add")));
205
206static 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 */
225static 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
251out_cancel_msg:
252	genlmsg_cancel(msg, hdr);
253out_free_msg:
254	nlmsg_free(msg);
255
256	return ret;
257}
258
259int 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
266int 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
273int 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
280int 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
287int 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
294int 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
301int 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
311int 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
318int 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
328int 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
335int 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
343int 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
350int 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
359static 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
371static 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
389out_cancel_nest:
390	nla_nest_cancel(msg, start_tz);
391
392	return ret;
393}
394
395static 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
439out_cancel_nest:
440	mutex_unlock(&tz->lock);
441
442	return -EMSGSIZE;
443}
444
445static 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
471static 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
498static 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
512static 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;
529out_cancel_nest:
530	nla_nest_cancel(msg, start_cdev);
531
532	return ret;
533}
534
535static 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
543static 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
564out_cancel_msg:
565	genlmsg_cancel(skb, hdr);
566
567	return ret;
568}
569
570static 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
596out_cancel_msg:
597	genlmsg_cancel(msg, hdr);
598out_free_msg:
599	nlmsg_free(msg);
600
601	return ret;
602}
603
604static 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
632static 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
644int __init thermal_netlink_init(void)
645{
646	return genl_register_family(&thermal_gnl_family);
647}