Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: LGPL-2.1+
0002 // Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org>
0003 #define _GNU_SOURCE
0004 #include <errno.h>
0005 #include <stdio.h>
0006 #include <stdlib.h>
0007 #include <unistd.h>
0008 
0009 #include <thermal.h>
0010 #include "thermal_nl.h"
0011 
0012 static struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = {
0013     /* Thermal zone */
0014     [THERMAL_GENL_ATTR_TZ]                  = { .type = NLA_NESTED },
0015     [THERMAL_GENL_ATTR_TZ_ID]               = { .type = NLA_U32 },
0016     [THERMAL_GENL_ATTR_TZ_TEMP]             = { .type = NLA_U32 },
0017     [THERMAL_GENL_ATTR_TZ_TRIP]             = { .type = NLA_NESTED },
0018     [THERMAL_GENL_ATTR_TZ_TRIP_ID]          = { .type = NLA_U32 },
0019     [THERMAL_GENL_ATTR_TZ_TRIP_TEMP]        = { .type = NLA_U32 },
0020     [THERMAL_GENL_ATTR_TZ_TRIP_TYPE]        = { .type = NLA_U32 },
0021     [THERMAL_GENL_ATTR_TZ_TRIP_HYST]        = { .type = NLA_U32 },
0022     [THERMAL_GENL_ATTR_TZ_MODE]             = { .type = NLA_U32 },
0023     [THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT]      = { .type = NLA_U32 },
0024     [THERMAL_GENL_ATTR_TZ_NAME]             = { .type = NLA_STRING },
0025 
0026     /* Governor(s) */
0027     [THERMAL_GENL_ATTR_TZ_GOV]              = { .type = NLA_NESTED },
0028     [THERMAL_GENL_ATTR_TZ_GOV_NAME]         = { .type = NLA_STRING },
0029 
0030     /* Cooling devices */
0031     [THERMAL_GENL_ATTR_CDEV]                = { .type = NLA_NESTED },
0032     [THERMAL_GENL_ATTR_CDEV_ID]             = { .type = NLA_U32 },
0033     [THERMAL_GENL_ATTR_CDEV_CUR_STATE]      = { .type = NLA_U32 },
0034     [THERMAL_GENL_ATTR_CDEV_MAX_STATE]      = { .type = NLA_U32 },
0035     [THERMAL_GENL_ATTR_CDEV_NAME]           = { .type = NLA_STRING },
0036 };
0037 
0038 static int parse_tz_get(struct genl_info *info, struct thermal_zone **tz)
0039 {
0040     struct nlattr *attr;
0041     struct thermal_zone *__tz = NULL;
0042     size_t size = 0;
0043     int rem;
0044 
0045     nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_TZ], rem) {
0046 
0047         if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_ID) {
0048 
0049             size++;
0050 
0051             __tz = realloc(__tz, sizeof(*__tz) * (size + 2));
0052             if (!__tz)
0053                 return THERMAL_ERROR;
0054 
0055             __tz[size - 1].id = nla_get_u32(attr);
0056         }
0057 
0058 
0059         if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_NAME)
0060             nla_strlcpy(__tz[size - 1].name, attr,
0061                     THERMAL_NAME_LENGTH);
0062     }
0063 
0064     if (__tz)
0065         __tz[size].id = -1;
0066 
0067     *tz = __tz;
0068 
0069     return THERMAL_SUCCESS;
0070 }
0071 
0072 static int parse_cdev_get(struct genl_info *info, struct thermal_cdev **cdev)
0073 {
0074     struct nlattr *attr;
0075     struct thermal_cdev *__cdev = NULL;
0076     size_t size = 0;
0077     int rem;
0078 
0079     nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_CDEV], rem) {
0080 
0081         if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_ID) {
0082 
0083             size++;
0084 
0085             __cdev = realloc(__cdev, sizeof(*__cdev) * (size + 2));
0086             if (!__cdev)
0087                 return THERMAL_ERROR;
0088 
0089             __cdev[size - 1].id = nla_get_u32(attr);
0090         }
0091 
0092         if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_NAME) {
0093             nla_strlcpy(__cdev[size - 1].name, attr,
0094                     THERMAL_NAME_LENGTH);
0095         }
0096 
0097         if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_CUR_STATE)
0098             __cdev[size - 1].cur_state = nla_get_u32(attr);
0099 
0100         if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_MAX_STATE)
0101             __cdev[size - 1].max_state = nla_get_u32(attr);
0102     }
0103 
0104     if (__cdev)
0105         __cdev[size].id = -1;
0106 
0107     *cdev = __cdev;
0108 
0109     return THERMAL_SUCCESS;
0110 }
0111 
0112 static int parse_tz_get_trip(struct genl_info *info, struct thermal_zone *tz)
0113 {
0114     struct nlattr *attr;
0115     struct thermal_trip *__tt = NULL;
0116     size_t size = 0;
0117     int rem;
0118 
0119     nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_TZ_TRIP], rem) {
0120 
0121         if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_ID) {
0122 
0123             size++;
0124 
0125             __tt = realloc(__tt, sizeof(*__tt) * (size + 2));
0126             if (!__tt)
0127                 return THERMAL_ERROR;
0128 
0129             __tt[size - 1].id = nla_get_u32(attr);
0130         }
0131 
0132         if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_TYPE)
0133             __tt[size - 1].type = nla_get_u32(attr);
0134 
0135         if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_TEMP)
0136             __tt[size - 1].temp = nla_get_u32(attr);
0137 
0138         if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_HYST)
0139             __tt[size - 1].hyst = nla_get_u32(attr);
0140     }
0141 
0142     if (__tt)
0143         __tt[size].id = -1;
0144 
0145     tz->trip = __tt;
0146 
0147     return THERMAL_SUCCESS;
0148 }
0149 
0150 static int parse_tz_get_temp(struct genl_info *info, struct thermal_zone *tz)
0151 {
0152     int id = -1;
0153 
0154     if (info->attrs[THERMAL_GENL_ATTR_TZ_ID])
0155         id = nla_get_u32(info->attrs[THERMAL_GENL_ATTR_TZ_ID]);
0156 
0157     if (tz->id != id)
0158         return THERMAL_ERROR;
0159 
0160     if (info->attrs[THERMAL_GENL_ATTR_TZ_TEMP])
0161         tz->temp = nla_get_u32(info->attrs[THERMAL_GENL_ATTR_TZ_TEMP]);
0162 
0163     return THERMAL_SUCCESS;
0164 }
0165 
0166 static int parse_tz_get_gov(struct genl_info *info, struct thermal_zone *tz)
0167 {
0168     int id = -1;
0169 
0170     if (info->attrs[THERMAL_GENL_ATTR_TZ_ID])
0171         id = nla_get_u32(info->attrs[THERMAL_GENL_ATTR_TZ_ID]);
0172 
0173     if (tz->id != id)
0174         return THERMAL_ERROR;
0175 
0176     if (info->attrs[THERMAL_GENL_ATTR_TZ_GOV_NAME]) {
0177         nla_strlcpy(tz->governor,
0178                 info->attrs[THERMAL_GENL_ATTR_TZ_GOV_NAME],
0179                 THERMAL_NAME_LENGTH);
0180     }
0181 
0182     return THERMAL_SUCCESS;
0183 }
0184 
0185 static int handle_netlink(struct nl_cache_ops *unused,
0186               struct genl_cmd *cmd,
0187               struct genl_info *info, void *arg)
0188 {
0189     int ret;
0190 
0191     switch (cmd->c_id) {
0192 
0193     case THERMAL_GENL_CMD_TZ_GET_ID:
0194         ret = parse_tz_get(info, arg);
0195         break;
0196 
0197     case THERMAL_GENL_CMD_CDEV_GET:
0198         ret = parse_cdev_get(info, arg);
0199         break;
0200 
0201     case THERMAL_GENL_CMD_TZ_GET_TEMP:
0202         ret = parse_tz_get_temp(info, arg);
0203         break;
0204 
0205     case THERMAL_GENL_CMD_TZ_GET_TRIP:
0206         ret = parse_tz_get_trip(info, arg);
0207         break;
0208 
0209     case THERMAL_GENL_CMD_TZ_GET_GOV:
0210         ret = parse_tz_get_gov(info, arg);
0211         break;
0212 
0213     default:
0214         return THERMAL_ERROR;
0215     }
0216 
0217     return ret;
0218 }
0219 
0220 static struct genl_cmd thermal_cmds[] = {
0221     {
0222         .c_id       = THERMAL_GENL_CMD_TZ_GET_ID,
0223         .c_name     = (char *)"List thermal zones",
0224         .c_msg_parser   = handle_netlink,
0225         .c_maxattr  = THERMAL_GENL_ATTR_MAX,
0226         .c_attr_policy  = thermal_genl_policy,
0227     },
0228     {
0229         .c_id       = THERMAL_GENL_CMD_TZ_GET_GOV,
0230         .c_name     = (char *)"Get governor",
0231         .c_msg_parser   = handle_netlink,
0232         .c_maxattr  = THERMAL_GENL_ATTR_MAX,
0233         .c_attr_policy  = thermal_genl_policy,
0234     },
0235     {
0236         .c_id       = THERMAL_GENL_CMD_TZ_GET_TEMP,
0237         .c_name     = (char *)"Get thermal zone temperature",
0238         .c_msg_parser   = handle_netlink,
0239         .c_maxattr  = THERMAL_GENL_ATTR_MAX,
0240         .c_attr_policy  = thermal_genl_policy,
0241     },
0242     {
0243         .c_id       = THERMAL_GENL_CMD_TZ_GET_TRIP,
0244         .c_name     = (char *)"Get thermal zone trip points",
0245         .c_msg_parser   = handle_netlink,
0246         .c_maxattr  = THERMAL_GENL_ATTR_MAX,
0247         .c_attr_policy  = thermal_genl_policy,
0248     },
0249     {
0250         .c_id       = THERMAL_GENL_CMD_CDEV_GET,
0251         .c_name     = (char *)"Get cooling devices",
0252         .c_msg_parser   = handle_netlink,
0253         .c_maxattr  = THERMAL_GENL_ATTR_MAX,
0254         .c_attr_policy  = thermal_genl_policy,
0255     },
0256 };
0257 
0258 static struct genl_ops thermal_cmd_ops = {
0259     .o_name     = (char *)"thermal",
0260     .o_cmds     = thermal_cmds,
0261     .o_ncmds    = ARRAY_SIZE(thermal_cmds),
0262 };
0263 
0264 static thermal_error_t thermal_genl_auto(struct thermal_handler *th, int id, int cmd,
0265                      int flags, void *arg)
0266 {
0267     struct nl_msg *msg;
0268     void *hdr;
0269 
0270     msg = nlmsg_alloc();
0271     if (!msg)
0272         return THERMAL_ERROR;
0273 
0274     hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, thermal_cmd_ops.o_id,
0275               0, flags, cmd, THERMAL_GENL_VERSION);
0276     if (!hdr)
0277         return THERMAL_ERROR;
0278 
0279     if (id >= 0 && nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id))
0280         return THERMAL_ERROR;
0281 
0282     if (nl_send_msg(th->sk_cmd, th->cb_cmd, msg, genl_handle_msg, arg))
0283         return THERMAL_ERROR;
0284 
0285     nlmsg_free(msg);
0286 
0287     return THERMAL_SUCCESS;
0288 }
0289 
0290 thermal_error_t thermal_cmd_get_tz(struct thermal_handler *th, struct thermal_zone **tz)
0291 {
0292     return thermal_genl_auto(th, -1, THERMAL_GENL_CMD_TZ_GET_ID,
0293                  NLM_F_DUMP | NLM_F_ACK, tz);
0294 }
0295 
0296 thermal_error_t thermal_cmd_get_cdev(struct thermal_handler *th, struct thermal_cdev **tc)
0297 {
0298     return thermal_genl_auto(th, -1, THERMAL_GENL_CMD_CDEV_GET,
0299                  NLM_F_DUMP | NLM_F_ACK, tc);
0300 }
0301 
0302 thermal_error_t thermal_cmd_get_trip(struct thermal_handler *th, struct thermal_zone *tz)
0303 {
0304     return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_TRIP,
0305                  0, tz);
0306 }
0307 
0308 thermal_error_t thermal_cmd_get_governor(struct thermal_handler *th, struct thermal_zone *tz)
0309 {
0310     return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_GOV, 0, tz);
0311 }
0312 
0313 thermal_error_t thermal_cmd_get_temp(struct thermal_handler *th, struct thermal_zone *tz)
0314 {
0315     return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_TEMP, 0, tz);
0316 }
0317 
0318 thermal_error_t thermal_cmd_exit(struct thermal_handler *th)
0319 {
0320     if (genl_unregister_family(&thermal_cmd_ops))
0321         return THERMAL_ERROR;
0322 
0323     nl_thermal_disconnect(th->sk_cmd, th->cb_cmd);
0324 
0325     return THERMAL_SUCCESS;
0326 }
0327 
0328 thermal_error_t thermal_cmd_init(struct thermal_handler *th)
0329 {
0330     int ret;
0331     int family;
0332 
0333     if (nl_thermal_connect(&th->sk_cmd, &th->cb_cmd))
0334         return THERMAL_ERROR;
0335 
0336     ret = genl_register_family(&thermal_cmd_ops);
0337     if (ret)
0338         return THERMAL_ERROR;
0339 
0340     ret = genl_ops_resolve(th->sk_cmd, &thermal_cmd_ops);
0341     if (ret)
0342         return THERMAL_ERROR;
0343 
0344     family = genl_ctrl_resolve(th->sk_cmd, "nlctrl");
0345     if (family != GENL_ID_CTRL)
0346         return THERMAL_ERROR;
0347 
0348     return THERMAL_SUCCESS;
0349 }