diff options
author | Daniel Lezcano <daniel.lezcano@linaro.org> | 2021-06-04 16:47:02 +0200 |
---|---|---|
committer | Daniel Lezcano <daniel.lezcano@linaro.org> | 2021-06-04 16:47:02 +0200 |
commit | 06ba4a3adf2812f573cc858b21b8b574c81eb11f (patch) | |
tree | be3031e55130070a1c2e1b9edf98fe2f83b00a4d |
Initial commit
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
-rw-r--r-- | Makefile | 26 | ||||
-rw-r--r-- | commands.c | 373 | ||||
-rw-r--r-- | events.c | 163 | ||||
-rw-r--r-- | netlink.c | 213 | ||||
-rw-r--r-- | sampling.c | 70 | ||||
-rw-r--r-- | thermal.c | 113 | ||||
-rw-r--r-- | thermal.h | 243 | ||||
-rw-r--r-- | tst_thermal.c | 302 |
8 files changed, 1503 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6b4c6f9 --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: LGPL-2.1+ +CC=gcc +CFLAGS=-g -Wall -I/usr/include/libnl3 -fPIC -Wextra -O2 +LDFLAGS=-lnl-genl-3 -lnl-3 -shared +DEPS = thermal.h +OBJS = thermal.o events.o sampling.o commands.o netlink.o +LIB=libthermal.so +C_BINS=tst_thermal.c + +BINS=$(C_BINS:.c=) + +default: libthermal.so + +tests: $(LIB) $(BINS) + +%.o: %.c $(DEPS) + $(CROSS_COMPILE)$(CC) -c -o $@ $< $(CFLAGS) + +$(LIB): $(OBJS) + $(CROSS_COMPILE)$(CC) $(CFLAGS) $(OBJS) -o $@ $(LDFLAGS) + +$(BINS): $(C_BINS) + $(CROSS_COMPILE)$(CC) $(CFLAGS) $< -o $@ -lthermal -L. -Wl,-rpath=. + +clean: + rm -f $(OBJS) $(LIB) $(BINS) *~ diff --git a/commands.c b/commands.c new file mode 100644 index 0000000..f9841b1 --- /dev/null +++ b/commands.c @@ -0,0 +1,373 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#define _GNU_SOURCE +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> + +#include <libnl3/netlink/genl/genl.h> +#include <libnl3/netlink/genl/mngt.h> +#include <libnl3/netlink/genl/ctrl.h> + +#include "thermal.h" + +static struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = { + /* Thermal zone */ + [THERMAL_GENL_ATTR_TZ] = { .type = NLA_NESTED }, + [THERMAL_GENL_ATTR_TZ_ID] = { .type = NLA_U32 }, + [THERMAL_GENL_ATTR_TZ_TEMP] = { .type = NLA_U32 }, + [THERMAL_GENL_ATTR_TZ_TRIP] = { .type = NLA_NESTED }, + [THERMAL_GENL_ATTR_TZ_TRIP_ID] = { .type = NLA_U32 }, + [THERMAL_GENL_ATTR_TZ_TRIP_TEMP] = { .type = NLA_U32 }, + [THERMAL_GENL_ATTR_TZ_TRIP_TYPE] = { .type = NLA_U32 }, + [THERMAL_GENL_ATTR_TZ_TRIP_HYST] = { .type = NLA_U32 }, + [THERMAL_GENL_ATTR_TZ_MODE] = { .type = NLA_U32 }, + [THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT] = { .type = NLA_U32 }, + [THERMAL_GENL_ATTR_TZ_NAME] = { .type = NLA_STRING }, + + /* Governor(s) */ + [THERMAL_GENL_ATTR_TZ_GOV] = { .type = NLA_NESTED }, + [THERMAL_GENL_ATTR_TZ_GOV_NAME] = { .type = NLA_STRING }, + + /* Cooling devices */ + [THERMAL_GENL_ATTR_CDEV] = { .type = NLA_NESTED }, + [THERMAL_GENL_ATTR_CDEV_ID] = { .type = NLA_U32 }, + [THERMAL_GENL_ATTR_CDEV_CUR_STATE] = { .type = NLA_U32 }, + [THERMAL_GENL_ATTR_CDEV_MAX_STATE] = { .type = NLA_U32 }, + [THERMAL_GENL_ATTR_CDEV_NAME] = { .type = NLA_STRING }, +}; + +int parse_tz_get(struct genl_info *info, struct thermal_zone **tz) +{ + struct nlattr *attr; + struct thermal_zone *__tz = NULL; + size_t size = 0; + int rem; + + nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_TZ], rem) { + + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_ID) { + + size++; + + __tz = realloc(__tz, sizeof(*__tz) * (size + 2)); + if (!__tz) { + fprintf(stderr, "Failed to allocate memory\n"); + return -1; + } + + __tz[size - 1].id = nla_get_u32(attr); + } + + + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_NAME) + nla_strlcpy(__tz[size - 1].name, attr, + THERMAL_NAME_LENGTH); + } + + /* + * We end the array of thermal zones + */ + __tz[size].id = -1; + + *tz = __tz; + + return 0; +} + +int parse_cdev_get(struct genl_info *info, struct thermal_cdev **cdev) +{ + struct nlattr *attr; + struct thermal_cdev *__cdev = NULL; + size_t size = 0; + int rem; + + nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_CDEV], rem) { + + if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_ID) { + + size++; + + __cdev = realloc(__cdev, sizeof(*__cdev) * (size + 2)); + if (!__cdev) { + fprintf(stderr, "Failed to allocate memory\n"); + return -1; + } + + __cdev[size - 1].id = nla_get_u32(attr); + } + + if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_NAME) { + nla_strlcpy(__cdev[size - 1].name, attr, + THERMAL_NAME_LENGTH); + } + + if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_CUR_STATE) { + __cdev[size - 1].cur_state = nla_get_u32(attr); + } + + if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_MAX_STATE) { + __cdev[size - 1].max_state = nla_get_u32(attr); + } + } + + __cdev[size].id = -1; + + *cdev = __cdev; + + return 0; +} + +int parse_tz_get_trip(struct genl_info *info, struct thermal_zone *tz) +{ + struct nlattr *attr; + struct thermal_trip *__tt = NULL; + size_t size = 0; + int rem; + + nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_TZ_TRIP], rem) { + + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_ID) { + + size++; + + __tt = realloc(__tt, sizeof(*__tt) * (size + 2)); + if (!__tt) { + fprintf(stderr, "Failed to allocate memory\n"); + return -1; + } + + __tt[size - 1].id = nla_get_u32(attr); + } + + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_TYPE) + __tt[size - 1].type = nla_get_u32(attr); + + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_TEMP) + __tt[size - 1].temp = nla_get_u32(attr); + + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_HYST) + __tt[size - 1].hyst = nla_get_u32(attr); + } + + __tt[size].id = -1; + + tz->trip = __tt; + + return 0; +} + +int parse_tz_get_temp(struct genl_info *info, struct thermal_zone *tz) +{ + int id = -1; + + if (info->attrs[THERMAL_GENL_ATTR_TZ_ID]) + id = nla_get_u32(info->attrs[THERMAL_GENL_ATTR_TZ_ID]); + + if (tz->id != id) { + fprintf(stderr, "thermal zone id mismatch '%d' <> '%d'\n", + tz->id, id); + return -1; + } + + if (info->attrs[THERMAL_GENL_ATTR_TZ_TEMP]) + tz->temp = nla_get_u32(info->attrs[THERMAL_GENL_ATTR_TZ_TEMP]); + + return 0; +} + +int parse_tz_get_gov(struct genl_info *info, struct thermal_zone *tz) +{ + int id = -1; + + if (info->attrs[THERMAL_GENL_ATTR_TZ_ID]) + id = nla_get_u32(info->attrs[THERMAL_GENL_ATTR_TZ_ID]); + + if (tz->id != id) { + fprintf(stderr, "thermal zone id mismatch '%d' <> '%d'\n", + tz->id, id); + return -1; + } + + if (info->attrs[THERMAL_GENL_ATTR_TZ_GOV_NAME]) { + nla_strlcpy(tz->governor, + info->attrs[THERMAL_GENL_ATTR_TZ_GOV_NAME], + THERMAL_NAME_LENGTH); + } + + return 0; +} + +static int handle_netlink(__maybe_unused struct nl_cache_ops *unused, + struct genl_cmd *cmd, + struct genl_info *info, void *arg) +{ + switch (cmd->c_id) { + + case THERMAL_GENL_CMD_TZ_GET: + parse_tz_get(info, arg); + break; + + case THERMAL_GENL_CMD_CDEV_GET: + parse_cdev_get(info, arg); + break; + + case THERMAL_GENL_CMD_TZ_GET_TEMP: + parse_tz_get_temp(info, arg); + break; + + case THERMAL_GENL_CMD_TZ_GET_TRIP: + parse_tz_get_trip(info, arg); + break; + + case THERMAL_GENL_CMD_TZ_GET_GOV: + parse_tz_get_gov(info, arg); + break; + + default: + printf("Unknown command id:%d\n", cmd->c_id); + }; + + return 0; +} + +static struct genl_cmd thermal_cmds[] = { + { + .c_id = THERMAL_GENL_CMD_TZ_GET, + .c_name = "List thermal zones", + .c_msg_parser = handle_netlink, + .c_maxattr = THERMAL_GENL_ATTR_MAX, + .c_attr_policy = thermal_genl_policy, + }, + { + .c_id = THERMAL_GENL_CMD_TZ_GET_GOV, + .c_name = "Get governor", + .c_msg_parser = handle_netlink, + .c_maxattr = THERMAL_GENL_ATTR_MAX, + .c_attr_policy = thermal_genl_policy, + }, + { + .c_id = THERMAL_GENL_CMD_TZ_GET_TEMP, + .c_name = "Get thermal zone temperature", + .c_msg_parser = handle_netlink, + .c_maxattr = THERMAL_GENL_ATTR_MAX, + .c_attr_policy = thermal_genl_policy, + }, + { + .c_id = THERMAL_GENL_CMD_TZ_GET_TRIP, + .c_name = "Get thermal zone trip points", + .c_msg_parser = handle_netlink, + .c_maxattr = THERMAL_GENL_ATTR_MAX, + .c_attr_policy = thermal_genl_policy, + }, + { + .c_id = THERMAL_GENL_CMD_CDEV_GET, + .c_name = "Get cooling devices", + .c_msg_parser = handle_netlink, + .c_maxattr = THERMAL_GENL_ATTR_MAX, + .c_attr_policy = thermal_genl_policy, + }, +}; + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +static struct genl_ops thermal_cmd_ops = { + .o_name = "thermal", + .o_cmds = thermal_cmds, + .o_ncmds = ARRAY_SIZE(thermal_cmds), +}; + +static int thermal_genl_auto(struct thermal_handler *th, int id, int cmd, + int flags, void *arg) +{ + struct nl_msg *msg; + void *hdr; + + msg = nlmsg_alloc(); + if (!msg) { + fprintf(stderr, "Failed to allocate message\n"); + return -1; + } + + hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, thermal_cmd_ops.o_id, + 0, flags, cmd, THERMAL_GENL_VERSION); + if (!hdr) { + fprintf(stderr, "Failed to set message\n"); + return -1; + } + + if (id >= 0 && nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id)) { + fprintf(stderr, "Failed to set tz id\n"); + return -1; + } + + if (nl_send_msg(th->sk_cmd, th->cb_cmd, msg, genl_handle_msg, arg)) { + fprintf(stderr, "Failed to send command\n"); + return -1; + } + + nlmsg_free(msg); + + return 0; +} + +int thermal_cmd_get_tz(struct thermal_handler *th, struct thermal_zone **tz) +{ + return thermal_genl_auto(th, -1, THERMAL_GENL_CMD_TZ_GET, + NLM_F_DUMP | NLM_F_ACK, tz); +} + +int thermal_cmd_get_cdev(struct thermal_handler *th, struct thermal_cdev **tc) +{ + return thermal_genl_auto(th, -1, THERMAL_GENL_CMD_CDEV_GET, + NLM_F_DUMP | NLM_F_ACK, tc); +} + +int thermal_cmd_get_trip(struct thermal_handler *th, struct thermal_zone *tz) +{ + return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_TRIP, + 0, tz); +} + +int thermal_cmd_get_governor(struct thermal_handler *th, struct thermal_zone *tz) +{ + return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_GOV, 0, tz); +} + +int thermal_cmd_get_temp(struct thermal_handler *th, struct thermal_zone *tz) +{ + return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_TEMP, 0, tz); +} + +int thermal_cmd_init(struct thermal_handler *th) +{ + int ret; + int family; + + if (nl_thermal_connect(&th->sk_cmd, &th->cb_cmd)) + return -1; + + ret = genl_register_family(&thermal_cmd_ops); + if (ret) { + fprintf(stderr, "genl_register_family: %d\n", ret); + return -1; + } + + ret = genl_ops_resolve(th->sk_cmd, &thermal_cmd_ops); + if (ret) { + fprintf(stderr, "genl_ops_resolve: %d\n", ret); + return -1; + } + + family = genl_ctrl_resolve(th->sk_cmd, "nlctrl"); + if (family != GENL_ID_CTRL) { + fprintf(stderr, "genl_ctrl_resolve: %d\n", family); + return -1; + } + + return 0; +} diff --git a/events.c b/events.c new file mode 100644 index 0000000..95117c9 --- /dev/null +++ b/events.c @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> + +#include <libnl3/netlink/genl/genl.h> +#include <libnl3/netlink/genl/mngt.h> +#include <libnl3/netlink/genl/ctrl.h> + +#include "thermal.h" + +/* + * Optimization: fill this array to tell which event we do want to pay + * attention to. That happens at init time with the ops + * structure. Each ops will enable the event and the general handler + * will be able to discard the event if there is not ops associated + * with it. + */ +static int enabled_ops[ __THERMAL_GENL_EVENT_MAX]; + +static int handle_thermal_event(struct nl_msg *n, void *arg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(n); + struct genlmsghdr *genlhdr = genlmsg_hdr(nlh); + struct nlattr *attrs[THERMAL_GENL_ATTR_MAX + 1]; + struct thermal_handler_param *thp = arg; + struct thermal_events_ops *ops = &thp->th->ops->events; + + genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL); + + arg = thp->arg; + + /* + * This is an event we don't care of, bail out. + */ + if (!enabled_ops[genlhdr->cmd]) + return 0; + + switch (genlhdr->cmd) { + + case THERMAL_GENL_EVENT_TZ_CREATE: + return ops->tz_create(nla_get_string(attrs[THERMAL_GENL_ATTR_TZ_NAME]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg); + + case THERMAL_GENL_EVENT_TZ_DELETE: + return ops->tz_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg); + + case THERMAL_GENL_EVENT_TZ_ENABLE: + return ops->tz_enable(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg); + + case THERMAL_GENL_EVENT_TZ_DISABLE: + return ops->tz_disable(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg); + + case THERMAL_GENL_EVENT_TZ_TRIP_CHANGE: + return ops->trip_change(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]), arg); + + case THERMAL_GENL_EVENT_TZ_TRIP_ADD: + return ops->trip_add(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]), arg); + + case THERMAL_GENL_EVENT_TZ_TRIP_DELETE: + return ops->trip_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]), arg); + + case THERMAL_GENL_EVENT_TZ_TRIP_HIGH: +/* return th->ops->trip_high(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP])); */ + return ops->trip_high(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]), -1, arg); + + + case THERMAL_GENL_EVENT_TZ_TRIP_LOW: + /* return th->ops->trip_low(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), */ + /* nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]), */ + /* nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP])); */ + return ops->trip_low(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]), -1, arg); + + case THERMAL_GENL_EVENT_TZ_CDEV_ADD: + return ops->cdev_add(nla_get_string(attrs[THERMAL_GENL_ATTR_CDEV_NAME]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_MAX_STATE]), arg); + + case THERMAL_GENL_EVENT_TZ_CDEV_DELETE: + return ops->cdev_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]), arg); + + case THERMAL_GENL_EVENT_TZ_CDEV_UPDATE: + return ops->cdev_update(nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_CUR_STATE]), arg); + + case THERMAL_GENL_EVENT_TZ_GOV_CHANGE: + return ops->gov_change(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), + nla_get_string(attrs[THERMAL_GENL_ATTR_GOV_NAME]), arg); + } + + return -1; +} + +static void thermal_events_ops_init(struct thermal_events_ops *ops) +{ + enabled_ops[THERMAL_GENL_EVENT_TZ_CREATE] = !!ops->tz_create; + enabled_ops[THERMAL_GENL_EVENT_TZ_DELETE] = !!ops->tz_delete; + enabled_ops[THERMAL_GENL_EVENT_TZ_DISABLE] = !!ops->tz_disable; + enabled_ops[THERMAL_GENL_EVENT_TZ_ENABLE] = !!ops->tz_enable; + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_HIGH] = !!ops->trip_high; + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_LOW] = !!ops->trip_low; + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_CHANGE] = !!ops->trip_change; + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_ADD] = !!ops->trip_add; + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DELETE] = !!ops->trip_delete; + enabled_ops[THERMAL_GENL_EVENT_TZ_CDEV_ADD] = !!ops->cdev_add; + enabled_ops[THERMAL_GENL_EVENT_TZ_CDEV_DELETE] = !!ops->cdev_delete; + enabled_ops[THERMAL_GENL_EVENT_TZ_CDEV_UPDATE] = !!ops->cdev_update; + enabled_ops[THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = !!ops->gov_change; +} + +int thermal_events_handle(struct thermal_handler *th, void *arg) +{ + struct thermal_handler_param thp = { .th = th, .arg = arg }; + + if (!th) + return -1; + + if (nl_cb_set(th->cb_event, NL_CB_VALID, NL_CB_CUSTOM, + handle_thermal_event, &thp)) + return -1; + + return nl_recvmsgs(th->sk_event, th->cb_event); +} + +int thermal_events_fd(struct thermal_handler *th) +{ + if (!th) + return -1; + + return nl_socket_get_fd(th->sk_event); +} + +int thermal_events_init(struct thermal_handler *th) +{ + thermal_events_ops_init(&th->ops->events); + + if (nl_thermal_connect(&th->sk_event, &th->cb_event)) + return -1; + + if (nl_subscribe_thermal(th->sk_event, th->cb_event, + THERMAL_GENL_EVENT_GROUP_NAME)) + return -1; + + return 0; +} diff --git a/netlink.c b/netlink.c new file mode 100644 index 0000000..2d78768 --- /dev/null +++ b/netlink.c @@ -0,0 +1,213 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <sys/epoll.h> + +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> + +#include <libnl3/netlink/genl/genl.h> +#include <libnl3/netlink/genl/mngt.h> +#include <libnl3/netlink/genl/ctrl.h> + +#include "thermal.h" + +struct handler_args { + const char *group; + int id; +}; + +static int err; +static int done; + +static int nl_seq_check_handler(struct nl_msg *msg, void *arg) +{ + return NL_OK; +} + +static int nl_error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) +{ + int *ret = arg; + + if (ret) + *ret = err->error; + + return NL_STOP; +} + +static int nl_finish_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + + if (ret) + *ret = 1; + + return NL_OK; +} + +static int nl_ack_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + + if (ret) + *ret = 1; + + return NL_OK; +} + +int nl_send_msg(struct nl_sock *sock, struct nl_cb *cb, struct nl_msg *msg, + int (*rx_handler)(struct nl_msg *, void *), void *data) +{ + if (!rx_handler) + return -1; + + err = nl_send_auto_complete(sock, msg); + if (err < 0) { + fprintf(stderr, "auto complete failed\n"); + return err; + } + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, rx_handler, data); + + err = done = 0; + + while (err == 0 && done == 0) + nl_recvmsgs(sock, cb); + + return err; +} + +static int nl_family_handler(struct nl_msg *msg, void *arg) +{ + struct handler_args *grp = arg; + struct nlattr *tb[CTRL_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *mcgrp; + int rem_mcgrp; + + nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[CTRL_ATTR_MCAST_GROUPS]) { + fprintf(stderr, "Multicast group not found\n"); + return -1; + } + + nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp) { + + struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1]; + + nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX, + nla_data(mcgrp), nla_len(mcgrp), NULL); + + if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] || + !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]) + continue; + + if (strncmp(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]), + grp->group, + nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]))) + continue; + + grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]); + + break; + } + + return 0; +} + +int nl_get_multicast_id(struct nl_sock *sock, struct nl_cb *cb, + const char *family, const char *group) +{ + struct nl_msg *msg; + int ret = 0, ctrlid; + struct handler_args grp = { + .group = group, + .id = -ENOENT, + }; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + ctrlid = genl_ctrl_resolve(sock, "nlctrl"); + + genlmsg_put(msg, 0, 0, ctrlid, 0, 0, CTRL_CMD_GETFAMILY, 0); + + nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, family); + + ret = nl_send_msg(sock, cb, msg, nl_family_handler, &grp); + if (ret) + goto nla_put_failure; + + ret = grp.id; + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + +int nl_thermal_connect(struct nl_sock **nl_sock, struct nl_cb **nl_cb) +{ + struct nl_cb *cb; + struct nl_sock *sock; + + cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!cb) + return -1; + + sock = nl_socket_alloc(); + if (!sock) + goto out_cb_free; + + if (genl_connect(sock)) + goto out_socket_free; + + if (nl_cb_err(cb, NL_CB_CUSTOM, nl_error_handler, &err) || + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, nl_finish_handler, &done) || + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, nl_ack_handler, &done) || + nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nl_seq_check_handler, &done)) + return -1; + + *nl_sock = sock; + *nl_cb = cb; + + return 0; + +out_socket_free: + nl_socket_free(sock); +out_cb_free: + nl_cb_put(cb); + return -1; +} + +void nl_thermal_disconnect(struct nl_sock *nl_sock, struct nl_cb *nl_cb) +{ + nl_close(nl_sock); + nl_socket_free(nl_sock); + nl_cb_put(nl_cb); +} + +int nl_subscribe_thermal(struct nl_sock *nl_sock, struct nl_cb *nl_cb, + const char *group) +{ + int mcid; + + mcid = nl_get_multicast_id(nl_sock, nl_cb, THERMAL_GENL_FAMILY_NAME, + group); + if (mcid < 0) { + fprintf(stderr, "Subscribing to multicast failed\n"); + return -1; + } + + if (nl_socket_add_membership(nl_sock, mcid)) + return -1; + + return 0; +} diff --git a/sampling.c b/sampling.c new file mode 100644 index 0000000..04e1259 --- /dev/null +++ b/sampling.c @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> + +#include <libnl3/netlink/genl/genl.h> +#include <libnl3/netlink/genl/mngt.h> +#include <libnl3/netlink/genl/ctrl.h> + +#include "thermal.h" + +static int handle_thermal_sample(struct nl_msg *n, void *arg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(n); + struct genlmsghdr *genlhdr = genlmsg_hdr(nlh); + struct nlattr *attrs[THERMAL_GENL_ATTR_MAX + 1]; + struct thermal_handler_param *thp = arg; + struct thermal_handler *th = thp->th; + + genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL); + + switch (genlhdr->cmd) { + + case THERMAL_GENL_SAMPLING_TEMP: + return th->ops->sampling.tz_temp( + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]), arg); + } + + return -1; +} + +int thermal_sampling_handle(struct thermal_handler *th, void *arg) +{ + struct thermal_handler_param thp = { .th = th, .arg = arg }; + + if (!th) + return -1; + + if (nl_cb_set(th->cb_sampling, NL_CB_VALID, NL_CB_CUSTOM, + handle_thermal_sample, &thp)) + return -1; + + return nl_recvmsgs(th->sk_sampling, th->cb_sampling); +} + +int thermal_sampling_fd(struct thermal_handler *th) +{ + if (!th) + return -1; + + return nl_socket_get_fd(th->sk_sampling); +} + +int thermal_sampling_init(struct thermal_handler *th) +{ + if (nl_thermal_connect(&th->sk_sampling, &th->cb_sampling)) + return -1; + + if (nl_subscribe_thermal(th->sk_sampling, th->cb_sampling, + THERMAL_GENL_SAMPLING_GROUP_NAME)) + return -1; + + return 0; +} diff --git a/thermal.c b/thermal.c new file mode 100644 index 0000000..bf121b9 --- /dev/null +++ b/thermal.c @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#include "thermal.h" + +int for_each_thermal_cdev(struct thermal_cdev *cdev, cb_tc_t cb, void *arg) +{ + int i, ret = 0; + + for (i = 0; cdev[i].id != -1; i++) + ret |= cb(&cdev[i], arg); + + return ret; +} + +int for_each_thermal_trip(struct thermal_trip *tt, cb_tt_t cb, void *arg) +{ + int i, ret = 0; + + for (i = 0; tt[i].id != -1; i++) + ret |= cb(&tt[i], arg); + + return ret; +} + +int for_each_thermal_zone(struct thermal_zone *tz, cb_tz_t cb, void *arg) +{ + int i, ret = 0; + + for (i = 0; tz[i].id != -1; i++) + ret |= cb(&tz[i], arg); + + return ret; +} + +struct thermal_zone *thermal_zone_find_by_name(struct thermal_zone *tz, + const char *name) +{ + int i; + + if (!name) + return NULL; + + for (i = 0; tz[i].id != -1; i++) { + if (!strcmp(tz[i].name, name)) + return &tz[i]; + } + + return NULL; +} + +struct thermal_zone *thermal_zone_find_by_id(struct thermal_zone *tz, int id) +{ + int i; + + if (id < 0) + return NULL; + + for (i = 0; tz[i].id != -1; i++) { + if (tz[i].id == id) + return &tz[i]; + } + + return NULL; +} + +static int __thermal_zone_discover(struct thermal_zone *tz, void *th) +{ + if (thermal_cmd_get_trip(th, tz) < 0) + return -1; + + if (thermal_cmd_get_governor(th, tz)) + return -1; + + return 0; +} + +struct thermal_zone *thermal_zone_discover(struct thermal_handler *th) +{ + struct thermal_zone *tz; + + if (thermal_cmd_get_tz(th, &tz) < 0) + return NULL; + + if (for_each_thermal_zone(tz, __thermal_zone_discover, th)) + return NULL; + + return tz; +} + +struct thermal_handler *thermal_init(struct thermal_ops *ops) +{ + struct thermal_handler *th; + + th = malloc(sizeof(*th)); + if (!th) + return NULL; + th->ops = ops; + + if (thermal_events_init(th)) + goto out_free; + + if (thermal_sampling_init(th)) + goto out_free; + + if (thermal_cmd_init(th)) + goto out_free; + + return th; + +out_free: + free(th); + + return NULL; +} diff --git a/thermal.h b/thermal.h new file mode 100644 index 0000000..853113d --- /dev/null +++ b/thermal.h @@ -0,0 +1,243 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#ifndef __THERMAL_H +#define __THERMAL_H + +#include <libnl3/netlink/genl/genl.h> +#include <libnl3/netlink/genl/mngt.h> +#include <libnl3/netlink/genl/ctrl.h> +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define __maybe_unused __attribute__((__unused__)) + +#ifndef THERMAL_NAME_LENGTH +#define THERMAL_NAME_LENGTH 64 +#endif + +struct thermal_sampling_ops { + int (*tz_temp)(int tz_id, int temp, void *arg); +}; + +struct thermal_events_ops { + int (*tz_create)(const char *name, int tz_id, void *arg); + int (*tz_delete)(int tz_id, void *arg); + int (*tz_enable)(int tz_id, void *arg); + int (*tz_disable)(int tz_id, void *arg); + int (*trip_high)(int tz_id, int trip_id, int temp, void *arg); + int (*trip_low)(int tz_id, int trip_id, int temp, void *arg); + int (*trip_add)(int tz_id, int trip_id, int type, int temp, int hyst, void *arg); + int (*trip_change)(int tz_id, int trip_id, int type, int temp, int hyst, void *arg); + int (*trip_delete)(int tz_id, int trip_id, void *arg); + int (*cdev_add)(const char *name, int cdev_id, int max_state, void *arg); + int (*cdev_delete)(int cdev_id, void *arg); + int (*cdev_update)(int cdev_id, int cur_state, void *arg); + int (*gov_change)(int tz_id, const char *gov_name, void *arg); +}; + +struct thermal_ops { + struct thermal_sampling_ops sampling; + struct thermal_events_ops events; +}; + +struct thermal_trip { + int id; + int type; + int temp; + int hyst; +}; + +struct thermal_zone { + int id; + int temp; + char name[THERMAL_NAME_LENGTH]; + char governor[THERMAL_NAME_LENGTH]; + struct thermal_trip *trip; +}; + +struct thermal_cdev { + int id; + char name[THERMAL_NAME_LENGTH]; + int max_state; + int min_state; + int cur_state; +}; + +struct thermal_handler { + int done; + int error; + struct thermal_ops *ops; + struct nl_msg *msg; + struct nl_sock *sk_event; + struct nl_sock *sk_sampling; + struct nl_sock *sk_cmd; + struct nl_cb *cb_cmd; + struct nl_cb *cb_event; + struct nl_cb *cb_sampling; +}; + +struct thermal_handler_param { + struct thermal_handler *th; + void *arg; +}; + +typedef int (*cb_tz_t)(struct thermal_zone *, void *); + +typedef int (*cb_tt_t)(struct thermal_trip *, void *); + +typedef int (*cb_tc_t)(struct thermal_cdev *, void *); + +int for_each_thermal_zone(struct thermal_zone *tz, cb_tz_t cb, void *arg); + +int for_each_thermal_trip(struct thermal_trip *tt, cb_tt_t cb, void *arg); + +int for_each_thermal_cdev(struct thermal_cdev *cdev, cb_tc_t cb, void *arg); + +struct thermal_zone *thermal_zone_find_by_name(struct thermal_zone *tz, + const char *name); + +struct thermal_zone *thermal_zone_find_by_id(struct thermal_zone *tz, int id); + +struct thermal_zone *thermal_zone_discover(struct thermal_handler *th); + +struct thermal_handler *thermal_init(struct thermal_ops *ops); + +/* + * Netlink thermal events + */ +extern int thermal_events_init(struct thermal_handler *th); + +extern int thermal_events_handle(struct thermal_handler *th, void *arg); + +extern int thermal_events_fd(struct thermal_handler *th); + +/* + * Netlink thermal commands + */ +extern int thermal_cmd_init(struct thermal_handler *th); + +extern int thermal_cmd_get_tz(struct thermal_handler *th, + struct thermal_zone **tz); + +extern int thermal_cmd_get_cdev(struct thermal_handler *th, + struct thermal_cdev **tc); + +extern int thermal_cmd_get_trip(struct thermal_handler *th, + struct thermal_zone *tz); + +extern int thermal_cmd_get_governor(struct thermal_handler *th, + struct thermal_zone *tz); + +extern int thermal_cmd_get_temp(struct thermal_handler *th, + struct thermal_zone *tz); + +/* + * Netlink thermal samples + */ +extern int thermal_sampling_init(struct thermal_handler *th); + +extern int thermal_sampling_handle(struct thermal_handler *th, void *arg); + +extern int thermal_sampling_fd(struct thermal_handler *th); + +/* + * Low level netlink + */ +extern int nl_subscribe_thermal(struct nl_sock *nl_sock, struct nl_cb *nl_cb, + const char *group); + +extern int nl_thermal_connect(struct nl_sock **nl_sock, struct nl_cb **nl_cb); + +extern void nl_thermal_disconnect(struct nl_sock *nl_sock, struct nl_cb *nl_cb); + +extern int nl_send_msg(struct nl_sock *sock, struct nl_cb *nl_cb, struct nl_msg *msg, + int (*rx_handler)(struct nl_msg *, void *), + void *data); + +#endif /* __THERMAL_H */ + +/* + * The netlink notification for thermal is new and the thermal uapi + * may be not installed yet. Provide the default values, so we can go + * forward until the linux-headers are available. + */ + +/* Adding event notification support elements */ +#define THERMAL_GENL_FAMILY_NAME "thermal" +#define THERMAL_GENL_VERSION 0x01 +#define THERMAL_GENL_SAMPLING_GROUP_NAME "sampling" +#define THERMAL_GENL_EVENT_GROUP_NAME "event" + +/* Attributes of thermal_genl_family */ +enum thermal_genl_attr { + THERMAL_GENL_ATTR_UNSPEC, + THERMAL_GENL_ATTR_TZ, + THERMAL_GENL_ATTR_TZ_ID, + THERMAL_GENL_ATTR_TZ_TEMP, + THERMAL_GENL_ATTR_TZ_TRIP, + THERMAL_GENL_ATTR_TZ_TRIP_ID, + THERMAL_GENL_ATTR_TZ_TRIP_TYPE, + THERMAL_GENL_ATTR_TZ_TRIP_TEMP, + THERMAL_GENL_ATTR_TZ_TRIP_HYST, + THERMAL_GENL_ATTR_TZ_MODE, + THERMAL_GENL_ATTR_TZ_NAME, + THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT, + THERMAL_GENL_ATTR_TZ_GOV, + THERMAL_GENL_ATTR_TZ_GOV_NAME, + THERMAL_GENL_ATTR_CDEV, + THERMAL_GENL_ATTR_CDEV_ID, + THERMAL_GENL_ATTR_CDEV_CUR_STATE, + THERMAL_GENL_ATTR_CDEV_MAX_STATE, + THERMAL_GENL_ATTR_CDEV_NAME, + THERMAL_GENL_ATTR_GOV_NAME, + + __THERMAL_GENL_ATTR_MAX, +}; +#define THERMAL_GENL_ATTR_MAX (__THERMAL_GENL_ATTR_MAX - 1) + +enum thermal_genl_sampling { + THERMAL_GENL_SAMPLING_TEMP, + __THERMAL_GENL_SAMPLING_MAX, +}; +#define THERMAL_GENL_SAMPLING_MAX (__THERMAL_GENL_SAMPLING_MAX - 1) + +/* Events of thermal_genl_family */ +enum thermal_genl_event { + THERMAL_GENL_EVENT_UNSPEC, + THERMAL_GENL_EVENT_TZ_CREATE,/* Thermal zone creation */ + THERMAL_GENL_EVENT_TZ_DELETE,/* Thermal zone deletion */ + THERMAL_GENL_EVENT_TZ_DISABLE,/* Thermal zone disabed */ + THERMAL_GENL_EVENT_TZ_ENABLE,/* Thermal zone enabled */ + THERMAL_GENL_EVENT_TZ_TRIP_HIGH,/* Trip point crossed the way up */ + THERMAL_GENL_EVENT_TZ_TRIP_LOW,/* Trip point crossed the way down */ + THERMAL_GENL_EVENT_TZ_TRIP_CHANGE,/* Trip point changed */ + THERMAL_GENL_EVENT_TZ_TRIP_ADD,/* Trip point added */ + THERMAL_GENL_EVENT_TZ_TRIP_DELETE,/* Trip point deleted */ + THERMAL_GENL_EVENT_TZ_CDEV_ADD, /* Cdev bound to the thermal zone */ + THERMAL_GENL_EVENT_TZ_CDEV_DELETE,/* Cdev unbound */ + THERMAL_GENL_EVENT_TZ_CDEV_UPDATE,/* Cdev state updated */ + THERMAL_GENL_EVENT_TZ_GOV_CHANGE,/* Governor policy changed */ + __THERMAL_GENL_EVENT_MAX, +}; +#define THERMAL_GENL_EVENT_MAX (__THERMAL_GENL_EVENT_MAX - 1) + +/* Commands supported by the thermal_genl_family */ +enum thermal_genl_cmd { + THERMAL_GENL_CMD_UNSPEC, + THERMAL_GENL_CMD_TZ_GET,/* List thermal zones id */ + THERMAL_GENL_CMD_TZ_GET_TRIP,/* List of thermal trips */ + THERMAL_GENL_CMD_TZ_GET_TEMP,/* Get the thermal zone temperature */ + THERMAL_GENL_CMD_TZ_GET_GOV,/* Get the thermal zone governor */ + THERMAL_GENL_CMD_TZ_GET_MODE,/* Get the thermal zone mode */ + THERMAL_GENL_CMD_CDEV_GET,/* List of cdev id */ + __THERMAL_GENL_CMD_MAX, +}; +#define THERMAL_GENL_CMD_MAX (__THERMAL_GENL_CMD_MAX - 1) + +#ifdef __cplusplus +} +#endif diff --git a/tst_thermal.c b/tst_thermal.c new file mode 100644 index 0000000..ad5b5a1 --- /dev/null +++ b/tst_thermal.c @@ -0,0 +1,302 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> + +#include <sys/epoll.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "thermal.h" + +#define MAX_EVENTS 10 + +static int show_trip(struct thermal_trip *tt, __maybe_unused void *arg) +{ + printf("trip id=%d, type=%d, temp=%d, hyst=%d\n", + tt->id, tt->type, tt->temp, tt->hyst); + + return 0; +} + +static int show_temp(struct thermal_zone *tz, __maybe_unused void *arg) +{ + thermal_cmd_get_temp(arg, tz); + + printf("temperature: %d\n", tz->temp); + + return 0; +} + +static int show_governor(struct thermal_zone *tz, __maybe_unused void *arg) +{ + thermal_cmd_get_governor(arg, tz); + + printf("governor: '%s'\n", tz->governor); + + return 0; +} + +static int show_tz(struct thermal_zone *tz, __maybe_unused void *arg) +{ + printf("thermal zone '%s', id=%d\n", tz->name, tz->id); + + for_each_thermal_trip(tz->trip, show_trip, NULL); + + show_temp(tz, arg); + + show_governor(tz, arg); + + return 0; +} + +static int tz_create(const char *name, int tz_id, __maybe_unused void *arg) +{ + printf("Thermal zone '%s'/%d created\n", name, tz_id); + + return 0; +} + +static int tz_delete(int tz_id, __maybe_unused void *arg) +{ + printf("Thermal zone %d deleted\n", tz_id); + + return 0; +} + +static int tz_disable(int tz_id, __maybe_unused void *arg) +{ + printf("Thermal zone %d disabled\n", tz_id); + + return 0; +} + +static int tz_enable(int tz_id, __maybe_unused void *arg) +{ + printf("Thermal zone %d enabled\n", tz_id); + + return 0; +} + +static int tz_temp(int tz_id, int temp, __maybe_unused void *arg) +{ + printf("Thermal zone %d temperature: %d\n", tz_id, temp); + + return 0; +} + +static int trip_high(int tz_id, int trip_id, int temp, __maybe_unused void *arg) +{ + printf("Thermal zone %d: trip point %d crossed way up with %d °C\n", + tz_id, trip_id, temp); + + return 0; +} + +static int trip_low(int tz_id, int trip_id, int temp, __maybe_unused void *arg) +{ + printf("Thermal zone %d: trip point %d crossed way down with %d °C\n", + tz_id, trip_id, temp); + + return 0; +} + +static int trip_add(int tz_id, int trip_id, int type, int temp, int hyst, __maybe_unused void *arg) +{ + printf("Trip point added %d: id=%d, type=%d, temp=%d, hyst=%d\n", + tz_id, trip_id, type, temp, hyst); + + return 0; +} + +static int trip_delete(int tz_id, int trip_id, __maybe_unused void *arg) +{ + printf("Trip point deleted %d: id=%d\n", tz_id, trip_id); + + return 0; +} + +static int trip_change(int tz_id, int trip_id, int type, int temp, int hyst, __maybe_unused void *arg) +{ + printf("Trip point changed %d: id=%d, type=%d, temp=%d, hyst=%d\n", + tz_id, trip_id, type, temp, hyst); + + return 0; +} + +static int cdev_add(const char *name, int cdev_id, int max_state, __maybe_unused void *arg) +{ + printf("Cooling device '%s'/%d (max state=%d) added", + name, cdev_id, max_state); + + return 0; +} + +static int cdev_delete(int cdev_id, __maybe_unused void *arg) +{ + printf("Cooling device %d deleted", cdev_id); + + return 0; +} + +static int cdev_update(int cdev_id, int cur_state, __maybe_unused void *arg) +{ + printf("cdev:%d state:%d\n", cdev_id, cur_state); + + return 0; +} + +static int gov_change(int tz_id, const char *name, __maybe_unused void *arg) +{ + printf("tz %d, governor=%s\n", tz_id, name); + + return 0; +} + +static struct thermal_ops ops = { + .sampling.tz_temp = tz_temp, + .events.tz_create = tz_create, + .events.tz_delete = tz_delete, + .events.tz_disable = tz_disable, + .events.tz_enable = tz_enable, + .events.trip_high = trip_high, + .events.trip_low = trip_low, + .events.trip_add = trip_add, + .events.trip_delete = trip_delete, + .events.trip_change = trip_change, + .events.cdev_add = cdev_add, + .events.cdev_delete = cdev_delete, + .events.cdev_update = cdev_update, + .events.gov_change = gov_change +}; + +static int stop = 0; + +static void sighandler(__maybe_unused int sig) +{ + stop = 1; +}; + +int thermal_netlink_get_temp_bench(struct thermal_handler *th, + struct thermal_zone *tz) +{ + int nr_messages = 0; + int nr_secs = 5; + unsigned long long sum = 0; + + printf("Benchmarking netlink... wait %d secs\n", nr_secs); + + signal(SIGALRM, sighandler); + alarm(nr_secs); + while (!stop) { + thermal_cmd_get_temp(th, tz); + sum += tz->temp; + nr_messages++; + } + + printf("Temperature reading %d msg/sec (%llu usec/msg), avg temp=%llu\n", + nr_messages / nr_secs, 1000000ULL / (nr_messages / nr_secs), + sum / nr_messages); + + return 0; +} + +int thermal_sysfs_get_temp_bench(struct thermal_zone *tz) +{ + int nr_messages = 0; + int nr_secs = 5; + unsigned long long sum = 0; + char path[THERMAL_NAME_LENGTH]; + int fd; + + snprintf(path, THERMAL_NAME_LENGTH, + "/sys/class/thermal/thermal_zone%d/temp", tz->id); + + fd = open(path, O_RDONLY); + if (fd < 0) + return -1; + + printf("Benchmarking sysfs... wait %d secs\n", nr_secs); + + signal(SIGALRM, sighandler); + alarm(nr_secs); + stop = 0; + while (!stop) { + char buffer[128] = { 0 }; + + pread(fd, buffer, 127, 0); + sum += atoi(buffer); + nr_messages++; + } + + if (!nr_messages) { + fprintf(stderr, "No message read\n"); + return -1; + } + + printf("Temperature reading %d msg/sec (%llu usec/msg), avg temp=%llu\n", + nr_messages / nr_secs, 1000000ULL / (nr_messages / nr_secs), + sum / nr_messages); + + close(fd); + + return 0; +} + +int main(void) +{ + struct thermal_zone *tz; + struct thermal_handler *th; + struct epoll_event ev; + struct epoll_event events[MAX_EVENTS]; + int epollfd; + int nfds; + int i; + + th = thermal_init(&ops); + if (!th) + return -1; + + tz = thermal_zone_discover(th); + if (!tz) + return -1; + + thermal_netlink_get_temp_bench(th, tz); + + thermal_sysfs_get_temp_bench(tz); + + for_each_thermal_zone(tz, show_tz, th); + + epollfd = epoll_create1(0); + if (epollfd < 0) + return -1; + + ev.events = EPOLLIN; + ev.data.ptr = thermal_events_handle; + + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, thermal_events_fd(th), &ev) == -1) + return -1; + + ev.events = EPOLLIN; + ev.data.ptr = thermal_sampling_handle; + + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, thermal_sampling_fd(th), &ev) == -1) + return -1; + + while (1) { + + nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); + for (i = 0; i < nfds; i++) { + if (events[i].data.ptr == thermal_events_handle) { + thermal_events_handle(th, NULL); + } else if (events[i].data.ptr == thermal_sampling_handle) { + thermal_sampling_handle(th, NULL); + } + } + } + + return 0; +} |