0001
0002
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
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
0027 [THERMAL_GENL_ATTR_TZ_GOV] = { .type = NLA_NESTED },
0028 [THERMAL_GENL_ATTR_TZ_GOV_NAME] = { .type = NLA_STRING },
0029
0030
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 }