blob: 9299e7975f31f24f3904a8657f1c030fadba3445 [file] [log] [blame]
Daniel Lezcano1ce50e72020-07-06 12:55:37 +02001// 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;
Colin Ian King52674f52020-07-06 15:07:47 +0100549 int ret;
Daniel Lezcano1ce50e72020-07-06 12:55:37 +0200550 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
644static int __init thermal_netlink_init(void)
645{
646 return genl_register_family(&thermal_gnl_family);
647}
648core_initcall(thermal_netlink_init);