1 // SPDX-License-Identifier: LGPL-2.1+
2 // Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org>
11 #include "thermal_nl.h"
13 static struct nla_policy thermal_genl_policy
[THERMAL_GENL_ATTR_MAX
+ 1] = {
15 [THERMAL_GENL_ATTR_TZ
] = { .type
= NLA_NESTED
},
16 [THERMAL_GENL_ATTR_TZ_ID
] = { .type
= NLA_U32
},
17 [THERMAL_GENL_ATTR_TZ_TEMP
] = { .type
= NLA_U32
},
18 [THERMAL_GENL_ATTR_TZ_TRIP
] = { .type
= NLA_NESTED
},
19 [THERMAL_GENL_ATTR_TZ_TRIP_ID
] = { .type
= NLA_U32
},
20 [THERMAL_GENL_ATTR_TZ_TRIP_TEMP
] = { .type
= NLA_U32
},
21 [THERMAL_GENL_ATTR_TZ_TRIP_TYPE
] = { .type
= NLA_U32
},
22 [THERMAL_GENL_ATTR_TZ_TRIP_HYST
] = { .type
= NLA_U32
},
23 [THERMAL_GENL_ATTR_TZ_MODE
] = { .type
= NLA_U32
},
24 [THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT
] = { .type
= NLA_U32
},
25 [THERMAL_GENL_ATTR_TZ_NAME
] = { .type
= NLA_STRING
},
28 [THERMAL_GENL_ATTR_TZ_GOV
] = { .type
= NLA_NESTED
},
29 [THERMAL_GENL_ATTR_TZ_GOV_NAME
] = { .type
= NLA_STRING
},
32 [THERMAL_GENL_ATTR_CDEV
] = { .type
= NLA_NESTED
},
33 [THERMAL_GENL_ATTR_CDEV_ID
] = { .type
= NLA_U32
},
34 [THERMAL_GENL_ATTR_CDEV_CUR_STATE
] = { .type
= NLA_U32
},
35 [THERMAL_GENL_ATTR_CDEV_MAX_STATE
] = { .type
= NLA_U32
},
36 [THERMAL_GENL_ATTR_CDEV_NAME
] = { .type
= NLA_STRING
},
39 [THERMAL_GENL_ATTR_THRESHOLD
] = { .type
= NLA_NESTED
},
40 [THERMAL_GENL_ATTR_THRESHOLD_TEMP
] = { .type
= NLA_U32
},
41 [THERMAL_GENL_ATTR_THRESHOLD_DIRECTION
] = { .type
= NLA_U32
},
44 static int parse_tz_get(struct genl_info
*info
, struct thermal_zone
**tz
)
47 struct thermal_zone
*__tz
= NULL
;
51 nla_for_each_nested(attr
, info
->attrs
[THERMAL_GENL_ATTR_TZ
], rem
) {
53 if (nla_type(attr
) == THERMAL_GENL_ATTR_TZ_ID
) {
57 __tz
= realloc(__tz
, sizeof(*__tz
) * (size
+ 2));
61 __tz
[size
- 1].id
= nla_get_u32(attr
);
65 if (nla_type(attr
) == THERMAL_GENL_ATTR_TZ_NAME
)
66 nla_strlcpy(__tz
[size
- 1].name
, attr
,
75 return THERMAL_SUCCESS
;
78 static int parse_cdev_get(struct genl_info
*info
, struct thermal_cdev
**cdev
)
81 struct thermal_cdev
*__cdev
= NULL
;
85 nla_for_each_nested(attr
, info
->attrs
[THERMAL_GENL_ATTR_CDEV
], rem
) {
87 if (nla_type(attr
) == THERMAL_GENL_ATTR_CDEV_ID
) {
91 __cdev
= realloc(__cdev
, sizeof(*__cdev
) * (size
+ 2));
95 __cdev
[size
- 1].id
= nla_get_u32(attr
);
98 if (nla_type(attr
) == THERMAL_GENL_ATTR_CDEV_NAME
) {
99 nla_strlcpy(__cdev
[size
- 1].name
, attr
,
100 THERMAL_NAME_LENGTH
);
103 if (nla_type(attr
) == THERMAL_GENL_ATTR_CDEV_CUR_STATE
)
104 __cdev
[size
- 1].cur_state
= nla_get_u32(attr
);
106 if (nla_type(attr
) == THERMAL_GENL_ATTR_CDEV_MAX_STATE
)
107 __cdev
[size
- 1].max_state
= nla_get_u32(attr
);
111 __cdev
[size
].id
= -1;
115 return THERMAL_SUCCESS
;
118 static int parse_tz_get_trip(struct genl_info
*info
, struct thermal_zone
*tz
)
121 struct thermal_trip
*__tt
= NULL
;
125 nla_for_each_nested(attr
, info
->attrs
[THERMAL_GENL_ATTR_TZ_TRIP
], rem
) {
127 if (nla_type(attr
) == THERMAL_GENL_ATTR_TZ_TRIP_ID
) {
131 __tt
= realloc(__tt
, sizeof(*__tt
) * (size
+ 2));
133 return THERMAL_ERROR
;
135 __tt
[size
- 1].id
= nla_get_u32(attr
);
138 if (nla_type(attr
) == THERMAL_GENL_ATTR_TZ_TRIP_TYPE
)
139 __tt
[size
- 1].type
= nla_get_u32(attr
);
141 if (nla_type(attr
) == THERMAL_GENL_ATTR_TZ_TRIP_TEMP
)
142 __tt
[size
- 1].temp
= nla_get_u32(attr
);
144 if (nla_type(attr
) == THERMAL_GENL_ATTR_TZ_TRIP_HYST
)
145 __tt
[size
- 1].hyst
= nla_get_u32(attr
);
153 return THERMAL_SUCCESS
;
156 static int parse_tz_get_temp(struct genl_info
*info
, struct thermal_zone
*tz
)
160 if (info
->attrs
[THERMAL_GENL_ATTR_TZ_ID
])
161 id
= nla_get_u32(info
->attrs
[THERMAL_GENL_ATTR_TZ_ID
]);
164 return THERMAL_ERROR
;
166 if (info
->attrs
[THERMAL_GENL_ATTR_TZ_TEMP
])
167 tz
->temp
= nla_get_u32(info
->attrs
[THERMAL_GENL_ATTR_TZ_TEMP
]);
169 return THERMAL_SUCCESS
;
172 static int parse_tz_get_gov(struct genl_info
*info
, struct thermal_zone
*tz
)
176 if (info
->attrs
[THERMAL_GENL_ATTR_TZ_ID
])
177 id
= nla_get_u32(info
->attrs
[THERMAL_GENL_ATTR_TZ_ID
]);
180 return THERMAL_ERROR
;
182 if (info
->attrs
[THERMAL_GENL_ATTR_TZ_GOV_NAME
]) {
183 nla_strlcpy(tz
->governor
,
184 info
->attrs
[THERMAL_GENL_ATTR_TZ_GOV_NAME
],
185 THERMAL_NAME_LENGTH
);
188 return THERMAL_SUCCESS
;
191 static int parse_threshold_get(struct genl_info
*info
, struct thermal_zone
*tz
)
194 struct thermal_threshold
*__tt
= NULL
;
199 * The size contains the size of the array and we want to
200 * access the last element, size - 1.
202 * The variable size is initialized to zero but it will be
203 * then incremented by the first if() statement. The message
204 * attributes are ordered, so the first if() statement will be
205 * always called before the second one. If it happens that is
206 * not the case, then it is a kernel bug.
208 nla_for_each_nested(attr
, info
->attrs
[THERMAL_GENL_ATTR_THRESHOLD
], rem
) {
210 if (nla_type(attr
) == THERMAL_GENL_ATTR_THRESHOLD_TEMP
) {
214 __tt
= realloc(__tt
, sizeof(*__tt
) * (size
+ 2));
216 return THERMAL_ERROR
;
218 __tt
[size
- 1].temperature
= nla_get_u32(attr
);
221 if (nla_type(attr
) == THERMAL_GENL_ATTR_THRESHOLD_DIRECTION
)
222 __tt
[size
- 1].direction
= nla_get_u32(attr
);
226 __tt
[size
].temperature
= INT_MAX
;
228 tz
->thresholds
= __tt
;
230 return THERMAL_SUCCESS
;
233 static int handle_netlink(struct nl_cache_ops
*unused
,
234 struct genl_cmd
*cmd
,
235 struct genl_info
*info
, void *arg
)
241 case THERMAL_GENL_CMD_TZ_GET_ID
:
242 ret
= parse_tz_get(info
, arg
);
245 case THERMAL_GENL_CMD_CDEV_GET
:
246 ret
= parse_cdev_get(info
, arg
);
249 case THERMAL_GENL_CMD_TZ_GET_TEMP
:
250 ret
= parse_tz_get_temp(info
, arg
);
253 case THERMAL_GENL_CMD_TZ_GET_TRIP
:
254 ret
= parse_tz_get_trip(info
, arg
);
257 case THERMAL_GENL_CMD_TZ_GET_GOV
:
258 ret
= parse_tz_get_gov(info
, arg
);
261 case THERMAL_GENL_CMD_THRESHOLD_GET
:
262 ret
= parse_threshold_get(info
, arg
);
266 return THERMAL_ERROR
;
272 static struct genl_cmd thermal_cmds
[] = {
274 .c_id
= THERMAL_GENL_CMD_TZ_GET_ID
,
275 .c_name
= (char *)"List thermal zones",
276 .c_msg_parser
= handle_netlink
,
277 .c_maxattr
= THERMAL_GENL_ATTR_MAX
,
278 .c_attr_policy
= thermal_genl_policy
,
281 .c_id
= THERMAL_GENL_CMD_TZ_GET_GOV
,
282 .c_name
= (char *)"Get governor",
283 .c_msg_parser
= handle_netlink
,
284 .c_maxattr
= THERMAL_GENL_ATTR_MAX
,
285 .c_attr_policy
= thermal_genl_policy
,
288 .c_id
= THERMAL_GENL_CMD_TZ_GET_TEMP
,
289 .c_name
= (char *)"Get thermal zone temperature",
290 .c_msg_parser
= handle_netlink
,
291 .c_maxattr
= THERMAL_GENL_ATTR_MAX
,
292 .c_attr_policy
= thermal_genl_policy
,
295 .c_id
= THERMAL_GENL_CMD_TZ_GET_TRIP
,
296 .c_name
= (char *)"Get thermal zone trip points",
297 .c_msg_parser
= handle_netlink
,
298 .c_maxattr
= THERMAL_GENL_ATTR_MAX
,
299 .c_attr_policy
= thermal_genl_policy
,
302 .c_id
= THERMAL_GENL_CMD_CDEV_GET
,
303 .c_name
= (char *)"Get cooling devices",
304 .c_msg_parser
= handle_netlink
,
305 .c_maxattr
= THERMAL_GENL_ATTR_MAX
,
306 .c_attr_policy
= thermal_genl_policy
,
309 .c_id
= THERMAL_GENL_CMD_THRESHOLD_GET
,
310 .c_name
= (char *)"Get thresholds list",
311 .c_msg_parser
= handle_netlink
,
312 .c_maxattr
= THERMAL_GENL_ATTR_MAX
,
313 .c_attr_policy
= thermal_genl_policy
,
316 .c_id
= THERMAL_GENL_CMD_THRESHOLD_ADD
,
317 .c_name
= (char *)"Add a threshold",
318 .c_msg_parser
= handle_netlink
,
319 .c_maxattr
= THERMAL_GENL_ATTR_MAX
,
320 .c_attr_policy
= thermal_genl_policy
,
323 .c_id
= THERMAL_GENL_CMD_THRESHOLD_DELETE
,
324 .c_name
= (char *)"Delete a threshold",
325 .c_msg_parser
= handle_netlink
,
326 .c_maxattr
= THERMAL_GENL_ATTR_MAX
,
327 .c_attr_policy
= thermal_genl_policy
,
330 .c_id
= THERMAL_GENL_CMD_THRESHOLD_FLUSH
,
331 .c_name
= (char *)"Flush the thresholds",
332 .c_msg_parser
= handle_netlink
,
333 .c_maxattr
= THERMAL_GENL_ATTR_MAX
,
334 .c_attr_policy
= thermal_genl_policy
,
338 static struct genl_ops thermal_cmd_ops
= {
339 .o_name
= (char *)"thermal",
340 .o_cmds
= thermal_cmds
,
341 .o_ncmds
= ARRAY_SIZE(thermal_cmds
),
350 typedef int (*cmd_cb_t
)(struct nl_msg
*, struct cmd_param
*);
352 static int thermal_genl_tz_id_encode(struct nl_msg
*msg
, struct cmd_param
*p
)
354 if (nla_put_u32(msg
, THERMAL_GENL_ATTR_TZ_ID
, p
->tz_id
))
360 static int thermal_genl_threshold_encode(struct nl_msg
*msg
, struct cmd_param
*p
)
362 if (thermal_genl_tz_id_encode(msg
, p
))
365 if (nla_put_u32(msg
, THERMAL_GENL_ATTR_THRESHOLD_TEMP
, p
->temp
))
368 if (nla_put_u32(msg
, THERMAL_GENL_ATTR_THRESHOLD_DIRECTION
, p
->direction
))
374 static thermal_error_t
thermal_genl_auto(struct thermal_handler
*th
, cmd_cb_t cmd_cb
,
375 struct cmd_param
*param
,
376 int cmd
, int flags
, void *arg
)
378 thermal_error_t ret
= THERMAL_ERROR
;
384 return THERMAL_ERROR
;
386 hdr
= genlmsg_put(msg
, NL_AUTO_PORT
, NL_AUTO_SEQ
, thermal_cmd_ops
.o_id
,
387 0, flags
, cmd
, THERMAL_GENL_VERSION
);
391 if (cmd_cb
&& cmd_cb(msg
, param
))
394 if (nl_send_msg(th
->sk_cmd
, th
->cb_cmd
, msg
, genl_handle_msg
, arg
))
397 ret
= THERMAL_SUCCESS
;
404 thermal_error_t
thermal_cmd_get_tz(struct thermal_handler
*th
, struct thermal_zone
**tz
)
406 return thermal_genl_auto(th
, NULL
, NULL
, THERMAL_GENL_CMD_TZ_GET_ID
,
407 NLM_F_DUMP
| NLM_F_ACK
, tz
);
410 thermal_error_t
thermal_cmd_get_cdev(struct thermal_handler
*th
, struct thermal_cdev
**tc
)
412 return thermal_genl_auto(th
, NULL
, NULL
, THERMAL_GENL_CMD_CDEV_GET
,
413 NLM_F_DUMP
| NLM_F_ACK
, tc
);
416 thermal_error_t
thermal_cmd_get_trip(struct thermal_handler
*th
, struct thermal_zone
*tz
)
418 struct cmd_param p
= { .tz_id
= tz
->id
};
420 return thermal_genl_auto(th
, thermal_genl_tz_id_encode
, &p
,
421 THERMAL_GENL_CMD_TZ_GET_TRIP
, 0, tz
);
424 thermal_error_t
thermal_cmd_get_governor(struct thermal_handler
*th
, struct thermal_zone
*tz
)
426 struct cmd_param p
= { .tz_id
= tz
->id
};
428 return thermal_genl_auto(th
, thermal_genl_tz_id_encode
, &p
,
429 THERMAL_GENL_CMD_TZ_GET_GOV
, 0, tz
);
432 thermal_error_t
thermal_cmd_get_temp(struct thermal_handler
*th
, struct thermal_zone
*tz
)
434 struct cmd_param p
= { .tz_id
= tz
->id
};
436 return thermal_genl_auto(th
, thermal_genl_tz_id_encode
, &p
,
437 THERMAL_GENL_CMD_TZ_GET_TEMP
, 0, tz
);
440 thermal_error_t
thermal_cmd_threshold_get(struct thermal_handler
*th
,
441 struct thermal_zone
*tz
)
443 struct cmd_param p
= { .tz_id
= tz
->id
};
445 return thermal_genl_auto(th
, thermal_genl_tz_id_encode
, &p
,
446 THERMAL_GENL_CMD_THRESHOLD_GET
, 0, tz
);
449 thermal_error_t
thermal_cmd_threshold_add(struct thermal_handler
*th
,
450 struct thermal_zone
*tz
,
454 struct cmd_param p
= { .tz_id
= tz
->id
, .temp
= temperature
, .direction
= direction
};
456 return thermal_genl_auto(th
, thermal_genl_threshold_encode
, &p
,
457 THERMAL_GENL_CMD_THRESHOLD_ADD
, 0, tz
);
460 thermal_error_t
thermal_cmd_threshold_delete(struct thermal_handler
*th
,
461 struct thermal_zone
*tz
,
465 struct cmd_param p
= { .tz_id
= tz
->id
, .temp
= temperature
, .direction
= direction
};
467 return thermal_genl_auto(th
, thermal_genl_threshold_encode
, &p
,
468 THERMAL_GENL_CMD_THRESHOLD_DELETE
, 0, tz
);
471 thermal_error_t
thermal_cmd_threshold_flush(struct thermal_handler
*th
,
472 struct thermal_zone
*tz
)
474 struct cmd_param p
= { .tz_id
= tz
->id
};
476 return thermal_genl_auto(th
, thermal_genl_tz_id_encode
, &p
,
477 THERMAL_GENL_CMD_THRESHOLD_FLUSH
, 0, tz
);
480 thermal_error_t
thermal_cmd_exit(struct thermal_handler
*th
)
482 if (genl_unregister_family(&thermal_cmd_ops
))
483 return THERMAL_ERROR
;
485 nl_thermal_disconnect(th
->sk_cmd
, th
->cb_cmd
);
487 return THERMAL_SUCCESS
;
490 thermal_error_t
thermal_cmd_init(struct thermal_handler
*th
)
495 if (nl_thermal_connect(&th
->sk_cmd
, &th
->cb_cmd
))
496 return THERMAL_ERROR
;
498 ret
= genl_register_family(&thermal_cmd_ops
);
500 return THERMAL_ERROR
;
502 ret
= genl_ops_resolve(th
->sk_cmd
, &thermal_cmd_ops
);
504 return THERMAL_ERROR
;
506 family
= genl_ctrl_resolve(th
->sk_cmd
, "nlctrl");
507 if (family
!= GENL_ID_CTRL
)
508 return THERMAL_ERROR
;
510 return THERMAL_SUCCESS
;