1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright 2020 Linaro Limited
5 * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
7 * Generic netlink for thermal management framework
9 #include <linux/module.h>
10 #include <linux/kernel.h>
11 #include <net/genetlink.h>
12 #include <uapi/linux/thermal.h>
14 #include "thermal_core.h"
16 static const struct genl_multicast_group thermal_genl_mcgrps
[] = {
17 { .name
= THERMAL_GENL_SAMPLING_GROUP_NAME
, },
18 { .name
= THERMAL_GENL_EVENT_GROUP_NAME
, },
21 static const struct nla_policy thermal_genl_policy
[THERMAL_GENL_ATTR_MAX
+ 1] = {
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
},
36 [THERMAL_GENL_ATTR_TZ_GOV
] = { .type
= NLA_NESTED
},
37 [THERMAL_GENL_ATTR_TZ_GOV_NAME
] = { .type
= NLA_STRING
,
38 .len
= THERMAL_NAME_LENGTH
},
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
},
49 struct nlattr
**attrs
;
63 typedef int (*cb_t
)(struct param
*);
65 static struct genl_family thermal_gnl_family
;
67 /************************** Sampling encoding *******************************/
69 int thermal_genl_sampling_temp(int id
, int temp
)
74 skb
= genlmsg_new(NLMSG_GOODSIZE
, GFP_KERNEL
);
78 hdr
= genlmsg_put(skb
, 0, 0, &thermal_gnl_family
, 0,
79 THERMAL_GENL_SAMPLING_TEMP
);
83 if (nla_put_u32(skb
, THERMAL_GENL_ATTR_TZ_ID
, id
))
86 if (nla_put_u32(skb
, THERMAL_GENL_ATTR_TZ_TEMP
, temp
))
89 genlmsg_end(skb
, hdr
);
91 genlmsg_multicast(&thermal_gnl_family
, skb
, 0, 0, GFP_KERNEL
);
95 genlmsg_cancel(skb
, hdr
);
102 /**************************** Event encoding *********************************/
104 static int thermal_genl_event_tz_create(struct param
*p
)
106 if (nla_put_u32(p
->msg
, THERMAL_GENL_ATTR_TZ_ID
, p
->tz_id
) ||
107 nla_put_string(p
->msg
, THERMAL_GENL_ATTR_TZ_NAME
, p
->name
))
113 static int thermal_genl_event_tz(struct param
*p
)
115 if (nla_put_u32(p
->msg
, THERMAL_GENL_ATTR_TZ_ID
, p
->tz_id
))
121 static int thermal_genl_event_tz_trip_up(struct param
*p
)
123 if (nla_put_u32(p
->msg
, THERMAL_GENL_ATTR_TZ_ID
, p
->tz_id
) ||
124 nla_put_u32(p
->msg
, THERMAL_GENL_ATTR_TZ_TRIP_ID
, p
->trip_id
))
130 static int thermal_genl_event_tz_trip_add(struct param
*p
)
132 if (nla_put_u32(p
->msg
, THERMAL_GENL_ATTR_TZ_ID
, p
->tz_id
) ||
133 nla_put_u32(p
->msg
, THERMAL_GENL_ATTR_TZ_TRIP_ID
, p
->trip_id
) ||
134 nla_put_u32(p
->msg
, THERMAL_GENL_ATTR_TZ_TRIP_TYPE
, p
->trip_type
) ||
135 nla_put_u32(p
->msg
, THERMAL_GENL_ATTR_TZ_TRIP_TEMP
, p
->trip_temp
) ||
136 nla_put_u32(p
->msg
, THERMAL_GENL_ATTR_TZ_TRIP_HYST
, p
->trip_hyst
))
142 static int thermal_genl_event_tz_trip_delete(struct param
*p
)
144 if (nla_put_u32(p
->msg
, THERMAL_GENL_ATTR_TZ_ID
, p
->tz_id
) ||
145 nla_put_u32(p
->msg
, THERMAL_GENL_ATTR_TZ_TRIP_ID
, p
->trip_id
))
151 static int thermal_genl_event_cdev_add(struct param
*p
)
153 if (nla_put_string(p
->msg
, THERMAL_GENL_ATTR_CDEV_NAME
,
155 nla_put_u32(p
->msg
, THERMAL_GENL_ATTR_CDEV_ID
,
157 nla_put_u32(p
->msg
, THERMAL_GENL_ATTR_CDEV_MAX_STATE
,
164 static int thermal_genl_event_cdev_delete(struct param
*p
)
166 if (nla_put_u32(p
->msg
, THERMAL_GENL_ATTR_CDEV_ID
, p
->cdev_id
))
172 static int thermal_genl_event_cdev_state_update(struct param
*p
)
174 if (nla_put_u32(p
->msg
, THERMAL_GENL_ATTR_CDEV_ID
,
176 nla_put_u32(p
->msg
, THERMAL_GENL_ATTR_CDEV_CUR_STATE
,
183 static int thermal_genl_event_gov_change(struct param
*p
)
185 if (nla_put_u32(p
->msg
, THERMAL_GENL_ATTR_TZ_ID
, p
->tz_id
) ||
186 nla_put_string(p
->msg
, THERMAL_GENL_ATTR_GOV_NAME
, p
->name
))
192 int thermal_genl_event_tz_delete(struct param
*p
)
193 __attribute__((alias("thermal_genl_event_tz")));
195 int thermal_genl_event_tz_enable(struct param
*p
)
196 __attribute__((alias("thermal_genl_event_tz")));
198 int thermal_genl_event_tz_disable(struct param
*p
)
199 __attribute__((alias("thermal_genl_event_tz")));
201 int thermal_genl_event_tz_trip_down(struct param
*p
)
202 __attribute__((alias("thermal_genl_event_tz_trip_up")));
204 int thermal_genl_event_tz_trip_change(struct param
*p
)
205 __attribute__((alias("thermal_genl_event_tz_trip_add")));
207 static cb_t event_cb
[] = {
208 [THERMAL_GENL_EVENT_TZ_CREATE
] = thermal_genl_event_tz_create
,
209 [THERMAL_GENL_EVENT_TZ_DELETE
] = thermal_genl_event_tz_delete
,
210 [THERMAL_GENL_EVENT_TZ_ENABLE
] = thermal_genl_event_tz_enable
,
211 [THERMAL_GENL_EVENT_TZ_DISABLE
] = thermal_genl_event_tz_disable
,
212 [THERMAL_GENL_EVENT_TZ_TRIP_UP
] = thermal_genl_event_tz_trip_up
,
213 [THERMAL_GENL_EVENT_TZ_TRIP_DOWN
] = thermal_genl_event_tz_trip_down
,
214 [THERMAL_GENL_EVENT_TZ_TRIP_CHANGE
] = thermal_genl_event_tz_trip_change
,
215 [THERMAL_GENL_EVENT_TZ_TRIP_ADD
] = thermal_genl_event_tz_trip_add
,
216 [THERMAL_GENL_EVENT_TZ_TRIP_DELETE
] = thermal_genl_event_tz_trip_delete
,
217 [THERMAL_GENL_EVENT_CDEV_ADD
] = thermal_genl_event_cdev_add
,
218 [THERMAL_GENL_EVENT_CDEV_DELETE
] = thermal_genl_event_cdev_delete
,
219 [THERMAL_GENL_EVENT_CDEV_STATE_UPDATE
] = thermal_genl_event_cdev_state_update
,
220 [THERMAL_GENL_EVENT_TZ_GOV_CHANGE
] = thermal_genl_event_gov_change
,
224 * Generic netlink event encoding
226 static int thermal_genl_send_event(enum thermal_genl_event event
,
233 msg
= genlmsg_new(NLMSG_GOODSIZE
, GFP_KERNEL
);
238 hdr
= genlmsg_put(msg
, 0, 0, &thermal_gnl_family
, 0, event
);
242 ret
= event_cb
[event
](p
);
246 genlmsg_end(msg
, hdr
);
248 genlmsg_multicast(&thermal_gnl_family
, msg
, 0, 1, GFP_KERNEL
);
253 genlmsg_cancel(msg
, hdr
);
260 int thermal_notify_tz_create(int tz_id
, const char *name
)
262 struct param p
= { .tz_id
= tz_id
, .name
= name
};
264 return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_CREATE
, &p
);
267 int thermal_notify_tz_delete(int tz_id
)
269 struct param p
= { .tz_id
= tz_id
};
271 return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DELETE
, &p
);
274 int thermal_notify_tz_enable(int tz_id
)
276 struct param p
= { .tz_id
= tz_id
};
278 return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_ENABLE
, &p
);
281 int thermal_notify_tz_disable(int tz_id
)
283 struct param p
= { .tz_id
= tz_id
};
285 return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DISABLE
, &p
);
288 int thermal_notify_tz_trip_down(int tz_id
, int trip_id
)
290 struct param p
= { .tz_id
= tz_id
, .trip_id
= trip_id
};
292 return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DOWN
, &p
);
295 int thermal_notify_tz_trip_up(int tz_id
, int trip_id
)
297 struct param p
= { .tz_id
= tz_id
, .trip_id
= trip_id
};
299 return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_UP
, &p
);
302 int thermal_notify_tz_trip_add(int tz_id
, int trip_id
, int trip_type
,
303 int trip_temp
, int trip_hyst
)
305 struct param p
= { .tz_id
= tz_id
, .trip_id
= trip_id
,
306 .trip_type
= trip_type
, .trip_temp
= trip_temp
,
307 .trip_hyst
= trip_hyst
};
309 return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_ADD
, &p
);
312 int thermal_notify_tz_trip_delete(int tz_id
, int trip_id
)
314 struct param p
= { .tz_id
= tz_id
, .trip_id
= trip_id
};
316 return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DELETE
, &p
);
319 int thermal_notify_tz_trip_change(int tz_id
, int trip_id
, int trip_type
,
320 int trip_temp
, int trip_hyst
)
322 struct param p
= { .tz_id
= tz_id
, .trip_id
= trip_id
,
323 .trip_type
= trip_type
, .trip_temp
= trip_temp
,
324 .trip_hyst
= trip_hyst
};
326 return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_CHANGE
, &p
);
329 int thermal_notify_cdev_state_update(int cdev_id
, int cdev_state
)
331 struct param p
= { .cdev_id
= cdev_id
, .cdev_state
= cdev_state
};
333 return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_STATE_UPDATE
, &p
);
336 int thermal_notify_cdev_add(int cdev_id
, const char *name
, int cdev_max_state
)
338 struct param p
= { .cdev_id
= cdev_id
, .name
= name
,
339 .cdev_max_state
= cdev_max_state
};
341 return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_ADD
, &p
);
344 int thermal_notify_cdev_delete(int cdev_id
)
346 struct param p
= { .cdev_id
= cdev_id
};
348 return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_DELETE
, &p
);
351 int thermal_notify_tz_gov_change(int tz_id
, const char *name
)
353 struct param p
= { .tz_id
= tz_id
, .name
= name
};
355 return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_GOV_CHANGE
, &p
);
358 /*************************** Command encoding ********************************/
360 static int __thermal_genl_cmd_tz_get_id(struct thermal_zone_device
*tz
,
363 struct sk_buff
*msg
= data
;
365 if (nla_put_u32(msg
, THERMAL_GENL_ATTR_TZ_ID
, tz
->id
) ||
366 nla_put_string(msg
, THERMAL_GENL_ATTR_TZ_NAME
, tz
->type
))
372 static int thermal_genl_cmd_tz_get_id(struct param
*p
)
374 struct sk_buff
*msg
= p
->msg
;
375 struct nlattr
*start_tz
;
378 start_tz
= nla_nest_start(msg
, THERMAL_GENL_ATTR_TZ
);
382 ret
= for_each_thermal_zone(__thermal_genl_cmd_tz_get_id
, msg
);
384 goto out_cancel_nest
;
386 nla_nest_end(msg
, start_tz
);
391 nla_nest_cancel(msg
, start_tz
);
396 static int thermal_genl_cmd_tz_get_trip(struct param
*p
)
398 struct sk_buff
*msg
= p
->msg
;
399 struct thermal_zone_device
*tz
;
400 struct nlattr
*start_trip
;
403 if (!p
->attrs
[THERMAL_GENL_ATTR_TZ_ID
])
406 id
= nla_get_u32(p
->attrs
[THERMAL_GENL_ATTR_TZ_ID
]);
408 tz
= thermal_zone_get_by_id(id
);
412 start_trip
= nla_nest_start(msg
, THERMAL_GENL_ATTR_TZ_TRIP
);
416 mutex_lock(&tz
->lock
);
418 for (i
= 0; i
< tz
->trips
; i
++) {
420 enum thermal_trip_type type
;
423 tz
->ops
->get_trip_type(tz
, i
, &type
);
424 tz
->ops
->get_trip_temp(tz
, i
, &temp
);
425 tz
->ops
->get_trip_hyst(tz
, i
, &hyst
);
427 if (nla_put_u32(msg
, THERMAL_GENL_ATTR_TZ_TRIP_ID
, i
) ||
428 nla_put_u32(msg
, THERMAL_GENL_ATTR_TZ_TRIP_TYPE
, type
) ||
429 nla_put_u32(msg
, THERMAL_GENL_ATTR_TZ_TRIP_TEMP
, temp
) ||
430 nla_put_u32(msg
, THERMAL_GENL_ATTR_TZ_TRIP_HYST
, hyst
))
431 goto out_cancel_nest
;
434 mutex_unlock(&tz
->lock
);
436 nla_nest_end(msg
, start_trip
);
441 mutex_unlock(&tz
->lock
);
446 static int thermal_genl_cmd_tz_get_temp(struct param
*p
)
448 struct sk_buff
*msg
= p
->msg
;
449 struct thermal_zone_device
*tz
;
452 if (!p
->attrs
[THERMAL_GENL_ATTR_TZ_ID
])
455 id
= nla_get_u32(p
->attrs
[THERMAL_GENL_ATTR_TZ_ID
]);
457 tz
= thermal_zone_get_by_id(id
);
461 ret
= thermal_zone_get_temp(tz
, &temp
);
465 if (nla_put_u32(msg
, THERMAL_GENL_ATTR_TZ_ID
, id
) ||
466 nla_put_u32(msg
, THERMAL_GENL_ATTR_TZ_TEMP
, temp
))
472 static int thermal_genl_cmd_tz_get_gov(struct param
*p
)
474 struct sk_buff
*msg
= p
->msg
;
475 struct thermal_zone_device
*tz
;
478 if (!p
->attrs
[THERMAL_GENL_ATTR_TZ_ID
])
481 id
= nla_get_u32(p
->attrs
[THERMAL_GENL_ATTR_TZ_ID
]);
483 tz
= thermal_zone_get_by_id(id
);
487 mutex_lock(&tz
->lock
);
489 if (nla_put_u32(msg
, THERMAL_GENL_ATTR_TZ_ID
, id
) ||
490 nla_put_string(msg
, THERMAL_GENL_ATTR_TZ_GOV_NAME
,
494 mutex_unlock(&tz
->lock
);
499 static int __thermal_genl_cmd_cdev_get(struct thermal_cooling_device
*cdev
,
502 struct sk_buff
*msg
= data
;
504 if (nla_put_u32(msg
, THERMAL_GENL_ATTR_CDEV_ID
, cdev
->id
))
507 if (nla_put_string(msg
, THERMAL_GENL_ATTR_CDEV_NAME
, cdev
->type
))
513 static int thermal_genl_cmd_cdev_get(struct param
*p
)
515 struct sk_buff
*msg
= p
->msg
;
516 struct nlattr
*start_cdev
;
519 start_cdev
= nla_nest_start(msg
, THERMAL_GENL_ATTR_CDEV
);
523 ret
= for_each_thermal_cooling_device(__thermal_genl_cmd_cdev_get
, msg
);
525 goto out_cancel_nest
;
527 nla_nest_end(msg
, start_cdev
);
531 nla_nest_cancel(msg
, start_cdev
);
536 static cb_t cmd_cb
[] = {
537 [THERMAL_GENL_CMD_TZ_GET_ID
] = thermal_genl_cmd_tz_get_id
,
538 [THERMAL_GENL_CMD_TZ_GET_TRIP
] = thermal_genl_cmd_tz_get_trip
,
539 [THERMAL_GENL_CMD_TZ_GET_TEMP
] = thermal_genl_cmd_tz_get_temp
,
540 [THERMAL_GENL_CMD_TZ_GET_GOV
] = thermal_genl_cmd_tz_get_gov
,
541 [THERMAL_GENL_CMD_CDEV_GET
] = thermal_genl_cmd_cdev_get
,
544 static int thermal_genl_cmd_dumpit(struct sk_buff
*skb
,
545 struct netlink_callback
*cb
)
547 struct param p
= { .msg
= skb
};
548 const struct genl_dumpit_info
*info
= genl_dumpit_info(cb
);
549 int cmd
= info
->op
.cmd
;
553 hdr
= genlmsg_put(skb
, 0, 0, &thermal_gnl_family
, 0, cmd
);
557 ret
= cmd_cb
[cmd
](&p
);
561 genlmsg_end(skb
, hdr
);
566 genlmsg_cancel(skb
, hdr
);
571 static int thermal_genl_cmd_doit(struct sk_buff
*skb
,
572 struct genl_info
*info
)
574 struct param p
= { .attrs
= info
->attrs
};
577 int cmd
= info
->genlhdr
->cmd
;
580 msg
= genlmsg_new(NLMSG_GOODSIZE
, GFP_KERNEL
);
585 hdr
= genlmsg_put_reply(msg
, info
, &thermal_gnl_family
, 0, cmd
);
589 ret
= cmd_cb
[cmd
](&p
);
593 genlmsg_end(msg
, hdr
);
595 return genlmsg_reply(msg
, info
);
598 genlmsg_cancel(msg
, hdr
);
605 static const struct genl_small_ops thermal_genl_ops
[] = {
607 .cmd
= THERMAL_GENL_CMD_TZ_GET_ID
,
608 .validate
= GENL_DONT_VALIDATE_STRICT
| GENL_DONT_VALIDATE_DUMP
,
609 .dumpit
= thermal_genl_cmd_dumpit
,
612 .cmd
= THERMAL_GENL_CMD_TZ_GET_TRIP
,
613 .validate
= GENL_DONT_VALIDATE_STRICT
| GENL_DONT_VALIDATE_DUMP
,
614 .doit
= thermal_genl_cmd_doit
,
617 .cmd
= THERMAL_GENL_CMD_TZ_GET_TEMP
,
618 .validate
= GENL_DONT_VALIDATE_STRICT
| GENL_DONT_VALIDATE_DUMP
,
619 .doit
= thermal_genl_cmd_doit
,
622 .cmd
= THERMAL_GENL_CMD_TZ_GET_GOV
,
623 .validate
= GENL_DONT_VALIDATE_STRICT
| GENL_DONT_VALIDATE_DUMP
,
624 .doit
= thermal_genl_cmd_doit
,
627 .cmd
= THERMAL_GENL_CMD_CDEV_GET
,
628 .validate
= GENL_DONT_VALIDATE_STRICT
| GENL_DONT_VALIDATE_DUMP
,
629 .dumpit
= thermal_genl_cmd_dumpit
,
633 static struct genl_family thermal_gnl_family __ro_after_init
= {
635 .name
= THERMAL_GENL_FAMILY_NAME
,
636 .version
= THERMAL_GENL_VERSION
,
637 .maxattr
= THERMAL_GENL_ATTR_MAX
,
638 .policy
= thermal_genl_policy
,
639 .small_ops
= thermal_genl_ops
,
640 .n_small_ops
= ARRAY_SIZE(thermal_genl_ops
),
641 .mcgrps
= thermal_genl_mcgrps
,
642 .n_mcgrps
= ARRAY_SIZE(thermal_genl_mcgrps
),
645 int __init
thermal_netlink_init(void)
647 return genl_register_family(&thermal_gnl_family
);