diff options
author | Daniel Lezcano <daniel.lezcano@linaro.org> | 2021-04-01 23:09:13 +0200 |
---|---|---|
committer | Daniel Lezcano <daniel.lezcano@linaro.org> | 2021-04-01 23:09:13 +0200 |
commit | 97367533428bb5c644aa9ecf42532d9bad3b9277 (patch) | |
tree | 46c1e579dba5d477bcb4ff8d0747ed12f32b07a2 | |
parent | b47b7db0e0fdde6cd75f8e72d149af04c1ab48f1 (diff) |
Initial import
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
-rw-r--r-- | src/libthermal/Makefile | 16 | ||||
-rw-r--r-- | src/libthermal/commands.c | 15 | ||||
-rw-r--r-- | src/libthermal/events.c | 140 | ||||
-rw-r--r-- | src/libthermal/netlink.c | 219 | ||||
-rw-r--r-- | src/libthermal/sampling.c | 53 | ||||
-rw-r--r-- | src/libthermal/thermal-cmd.c | 404 | ||||
-rw-r--r-- | src/libthermal/thermal-event.c | 261 | ||||
-rw-r--r-- | src/libthermal/thermal-genl.c | 534 | ||||
-rw-r--r-- | src/libthermal/thermal-sampling.c | 249 | ||||
-rw-r--r-- | src/libthermal/thermal.c | 193 | ||||
-rw-r--r-- | src/libthermal/thermal.h | 158 |
11 files changed, 2242 insertions, 0 deletions
diff --git a/src/libthermal/Makefile b/src/libthermal/Makefile new file mode 100644 index 0000000..7e57573 --- /dev/null +++ b/src/libthermal/Makefile @@ -0,0 +1,16 @@ +CC=gcc +CFLAGS=-g -Wall -I/usr/include/libnl3 +LDFLAGS=-lnl-genl-3 -lnl-3 +DEPS = thermal.h +OBJS = thermal.o events.o sampling.o netlink.o + +default: thermal + +%.o: %.c + $(CROSS_COMPILE)$(CC) -c -o $@ $< $(CFLAGS) + +thermal: $(OBJS) + $(CROSS_COMPILE)$(CC) $(CFLAGS) $(OBJS) -o $@ $(LDFLAGS) + +clean: + rm -f $(OBJS) thermal diff --git a/src/libthermal/commands.c b/src/libthermal/commands.c new file mode 100644 index 0000000..79ef0d4 --- /dev/null +++ b/src/libthermal/commands.c @@ -0,0 +1,15 @@ +#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" +f diff --git a/src/libthermal/events.c b/src/libthermal/events.c new file mode 100644 index 0000000..0d7c9a0 --- /dev/null +++ b/src/libthermal/events.c @@ -0,0 +1,140 @@ +#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 *th = arg; + + genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL); + + /* + * 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 th->ops->tz_create(nla_get_string(attrs[THERMAL_GENL_ATTR_TZ_NAME]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID])); + + case THERMAL_GENL_EVENT_TZ_DELETE: + return th->ops->tz_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID])); + + case THERMAL_GENL_EVENT_TZ_ENABLE: + return th->ops->tz_enable(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID])); + + case THERMAL_GENL_EVENT_TZ_DISABLE: + return th->ops->tz_disable(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID])); + + case THERMAL_GENL_EVENT_TZ_TRIP_CHANGE: + return th->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])); + + case THERMAL_GENL_EVENT_TZ_TRIP_ADD: + return th->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])); + + case THERMAL_GENL_EVENT_TZ_TRIP_DELETE: + return th->ops->trip_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])); + + 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])); + + 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])); + + case THERMAL_GENL_EVENT_TZ_CDEV_ADD: + return th->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])); + + case THERMAL_GENL_EVENT_TZ_CDEV_DELETE: + return th->ops->cdev_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID])); + + case THERMAL_GENL_EVENT_TZ_CDEV_UPDATE: + return th->ops->cdev_update(nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_CUR_STATE])); + + case THERMAL_GENL_EVENT_TZ_GOV_CHANGE: + return th->ops->gov_change(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), + nla_get_string(attrs[THERMAL_GENL_ATTR_GOV_NAME])); + } + + return -1; +} + +static void thermal_events_ops_init(struct thermal_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) +{ + return nl_recvmsgs(th->sk_event, th->cb_event); +} + +int thermal_events_fd(struct thermal_handler *th) +{ + return nl_socket_get_fd(th->sk_event); +} + +int thermal_events_init(struct thermal_handler *th) +{ + thermal_events_ops_init(th->ops); + + if (nl_thermal_connect(&th->sk_event, &th->cb_event, + handle_thermal_event, th)) + return -1; + + return nl_subscribe_thermal(th->sk_event, + THERMAL_GENL_EVENT_GROUP_NAME); +} diff --git a/src/libthermal/netlink.c b/src/libthermal/netlink.c new file mode 100644 index 0000000..0ef7ec5 --- /dev/null +++ b/src/libthermal/netlink.c @@ -0,0 +1,219 @@ +#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 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; +} + +static int nl_send_msg(struct nl_sock *sock, struct nl_msg *msg, + int (*rx_handler)(struct nl_msg *, void *), + void *data) +{ + struct nl_cb *cb; + int err, done; + + cb = nl_cb_alloc(NL_CB_DEFAULT); + if (cb == NULL) + return -ENOMEM; + + err = nl_send_auto_complete(sock, msg); + if (err < 0) { + nl_cb_put(cb); + return err; + } + + err = done = 0; + 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); + + if (rx_handler != NULL) + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, rx_handler, data); + + while (err == 0 && done == 0) + nl_recvmsgs(sock, cb); + + nl_cb_put(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, const char *family, + const char *group) +{ + struct nl_msg *msg; + int err = 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); + + err = nl_send_msg(sock, msg, nl_family_handler, &grp); + if (err) + goto nla_put_failure; + + err = grp.id; + +nla_put_failure: + nlmsg_free(msg); + return err; +} + +int nl_thermal_connect(struct nl_sock **nl_sock, struct nl_cb **nl_cb, + int (*handler)(struct nl_msg *n, void *arg), void *arg) +{ + 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, NULL) || + nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, + nl_seq_check_handler, NULL) || + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, + handler, arg)) + 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, const char *group) +{ + int mcid; + + mcid = nl_get_multicast_id(nl_sock, THERMAL_GENL_FAMILY_NAME, group); + if (mcid < 0) + return -1; + + if (nl_socket_add_membership(nl_sock, mcid)) + return -1; + + return 0; +} diff --git a/src/libthermal/sampling.c b/src/libthermal/sampling.c new file mode 100644 index 0000000..87ffe50 --- /dev/null +++ b/src/libthermal/sampling.c @@ -0,0 +1,53 @@ +#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 *th = arg; + + genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL); + + switch (genlhdr->cmd) { + + case THERMAL_GENL_SAMPLING_TEMP: + return th->ops->tz_temp(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP])); + } + + return -1; +} + +int thermal_sampling_handle(struct thermal_handler *th) +{ + return nl_recvmsgs(th->sk_sampling, th->cb_sampling); +} + +int thermal_sampling_fd(struct thermal_handler *th) +{ + 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, + handle_thermal_sample, th)) + return -1; + + return nl_subscribe_thermal(th->sk_sampling, + THERMAL_GENL_SAMPLING_GROUP_NAME); +} diff --git a/src/libthermal/thermal-cmd.c b/src/libthermal/thermal-cmd.c new file mode 100644 index 0000000..caba8f9 --- /dev/null +++ b/src/libthermal/thermal-cmd.c @@ -0,0 +1,404 @@ +#include <stdio.h> +#include <errno.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> + +#include "thermal.h" + +static int nl_error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) +{ + int *ret = arg; + + *ret = err->error; + + printf("%s\n", __func__); + + return NL_STOP; +} + +static int nl_finish_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + + *ret = 1; + + printf("%s\n", __func__); + + return NL_OK; +} + +static int nl_ack_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + + *ret = 1; + + printf("%s\n", __func__); + + return NL_OK; +} + +static int nl_overrun_handler(struct nl_msg *msg, void *arg) +{ + printf("%s\n", __func__); + + return NL_SKIP; +} + +static int nl_skipped_handler(struct nl_msg *msg, void *arg) +{ + printf("%s\n", __func__); + + return NL_SKIP; +} + +static int nl_seq_check_handler(struct nl_msg *msg, void *arg) +{ + printf("%s\n", __func__); + + return NL_OK; +} + +int show_tz(struct genl_info *info) +{ + struct nlattr *attr; + int rem; + + nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_TZ], rem) { + + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_ID) + printf("Thermal zone id: %d\n", nla_get_u32(attr)); + + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_NAME) + printf("Thermal zone name: %s\n", + nla_get_string(attr)); + } + + return 0; +} + +int show_cdev(struct genl_info *info) +{ + struct nlattr *attr; + int rem; + + nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_CDEV], rem) { + + if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_ID) + printf("Cooling device id: %d\n", nla_get_u32(attr)); + + if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_NAME) + printf("Cooling device name: %s\n", nla_get_string(attr)); + } + + return 0; +} + +int show_trip(struct genl_info *info) +{ + struct nlattr *attr; + 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) + printf("Trip id: %d\n", nla_get_u32(attr)); + + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_TYPE) + printf("Trip type: %d\n", nla_get_u32(attr)); + + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_TEMP) + printf("Trip temp: %d\n", nla_get_u32(attr)); + + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_HYST) + printf("Trip hyst: %d\n", nla_get_u32(attr)); + } + + return 0; +} + +int show_temp(struct genl_info *info) +{ + if (info->attrs[THERMAL_GENL_ATTR_TZ_ID]) + printf("Thermal zone id: %d\n", + nla_get_u32(info->attrs[THERMAL_GENL_ATTR_TZ_ID])); + + if (info->attrs[THERMAL_GENL_ATTR_TZ_TEMP]) + printf("Thermal zone temperature: %d\n", + nla_get_u32(info->attrs[THERMAL_GENL_ATTR_TZ_TEMP])); + + return 0; +} + +int show_gov(struct genl_info *info) +{ + if (info->attrs[THERMAL_GENL_ATTR_TZ_ID]) + printf("Thermal zone id: %d\n", + nla_get_u32(info->attrs[THERMAL_GENL_ATTR_TZ_ID])); + + if (info->attrs[THERMAL_GENL_ATTR_TZ_GOV_NAME]) + printf("Thermal zone governor: %s\n", + nla_get_string(info->attrs[THERMAL_GENL_ATTR_TZ_GOV_NAME])); + + return 0; +} + +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_MIN_STATE] = { .type = NLA_U32 }, + [THERMAL_GENL_ATTR_CDEV_NAME] = { .type = NLA_STRING }, +}; + +static int handle_netlink(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: + printf("THERMAL_GENL_CMD_TZ_GET\n"); + show_tz(info); + break; + + case THERMAL_GENL_CMD_CDEV_GET: + printf("THERMAL_GENL_CMD_CDEV_GET\n"); + show_cdev(info); + break; + + case THERMAL_GENL_CMD_TZ_GET_TEMP: + printf("THERMAL_GENL_CMD_TZ_GET_TEMP\n"); + show_temp(info); + break; + + case THERMAL_GENL_CMD_TZ_GET_TRIP: + printf("THERMAL_GENL_CMD_TZ_GET_TRIP\n"); + show_trip(info); + break; + + case THERMAL_GENL_CMD_TZ_GET_GOV: + printf("THERMAL_GENL_CMD_TZ_GET_GOV\n"); + show_gov(info); + break; + }; + + 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 nl_sock *sock, int family, int id, int cmd, int flags) +{ + 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, family, 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_auto_complete(sock, msg) < 0) { + fprintf(stderr, "Failed to nl_send_auto_complete\n"); + return -1; + } + + { + struct nl_cb *cb; + int err = 0, done = 0; + + cb = nl_cb_alloc(NL_CB_DEFAULT); + + 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_OVERRUN, NL_CB_CUSTOM, nl_overrun_handler, &done); + nl_cb_set(cb, NL_CB_SKIPPED, NL_CB_CUSTOM, nl_skipped_handler, &done); + nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nl_seq_check_handler, &done); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, genl_handle_msg, NULL); + + while (!done && !err) + nl_recvmsgs(sock, cb); + } + + return 0; +} + +struct handler_args { + const char *group; + int id; +}; + +int main(int argc, char *argv[]) +{ + struct nl_sock *sk_cmd; + int ret, family; + + nl_debug = 0; + + sk_cmd = nl_socket_alloc(); + if (!sk_cmd) { + perror("nl_socket_alloc"); + return -1; + } + + nl_socket_modify_cb(sk_cmd, NL_CB_VALID, NL_CB_CUSTOM, + genl_handle_msg, NULL); + + ret = genl_connect(sk_cmd); + if (ret) { + fprintf(stderr, "genl_connect: %d\n", ret); + 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(sk_cmd, &thermal_cmd_ops); + if (ret) { + fprintf(stderr, "genl_ops_resolve: %d\n", ret); + return -1; + } + + family = genl_ctrl_resolve(sk_cmd, "nlctrl"); + if (family != GENL_ID_CTRL) { + fprintf(stderr, "genl_ctrl_resolve: %d\n", family); + return -1; + } + + ret = thermal_genl_auto(sk_cmd, thermal_cmd_ops.o_id, -1, + THERMAL_GENL_CMD_TZ_GET, + NLM_F_DUMP | NLM_F_ACK); + if (ret < 0) { + fprintf(stderr, "Failed to thermal_genl_send: %d\n", ret); + return -1; + } + + ret = thermal_genl_auto(sk_cmd, thermal_cmd_ops.o_id, -1, + THERMAL_GENL_CMD_CDEV_GET, + NLM_F_DUMP | NLM_F_ACK); + if (ret < 0) { + fprintf(stderr, "Failed to thermal_genl_send: %d\n", ret); + return -1; + } + + ret = thermal_genl_auto(sk_cmd, thermal_cmd_ops.o_id, 0, + THERMAL_GENL_CMD_TZ_GET_TRIP, 0); + if (ret < 0) { + fprintf(stderr, "Failed to thermal_genl_send: %d\n", ret); + return -1; + } + + ret = thermal_genl_auto(sk_cmd, thermal_cmd_ops.o_id, 1, + THERMAL_GENL_CMD_TZ_GET_TRIP, 0); + if (ret < 0) { + fprintf(stderr, "Failed to thermal_genl_send: %d\n", ret); + return -1; + } + + ret = thermal_genl_auto(sk_cmd, thermal_cmd_ops.o_id, 0, + THERMAL_GENL_CMD_TZ_GET_TEMP, 0); + if (ret < 0) { + fprintf(stderr, "Failed to thermal_genl_send: %d\n", ret); + return -1; + } + + ret = thermal_genl_auto(sk_cmd, thermal_cmd_ops.o_id, 1, + THERMAL_GENL_CMD_TZ_GET_TEMP, 0); + if (ret < 0) { + fprintf(stderr, "Failed to thermal_genl_send: %d\n", ret); + return -1; + } + + ret = thermal_genl_auto(sk_cmd, thermal_cmd_ops.o_id, 0, + THERMAL_GENL_CMD_TZ_GET_GOV, 0); + if (ret < 0) { + fprintf(stderr, "Failed to thermal_genl_send: %d\n", ret); + return -1; + } + + return 0; +} diff --git a/src/libthermal/thermal-event.c b/src/libthermal/thermal-event.c new file mode 100644 index 0000000..98d645e --- /dev/null +++ b/src/libthermal/thermal-event.c @@ -0,0 +1,261 @@ +#include <stdio.h> +#include <errno.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> + +#include "thermal.h" + +static int nl_error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) +{ + int *ret = arg; + + *ret = err->error; + + printf("%s\n", __func__); + + return NL_STOP; +} + +static int nl_finish_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + + *ret = 1; + + /* printf("%s\n", __func__); */ + + return NL_OK; +} + +static int nl_ack_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + + *ret = 1; + + printf("%s\n", __func__); + + return NL_OK; +} + +static int nl_overrun_handler(struct nl_msg *msg, void *arg) +{ + printf("%s\n", __func__); + + return NL_SKIP; +} + +static int nl_skipped_handler(struct nl_msg *msg, void *arg) +{ + printf("%s\n", __func__); + + return NL_SKIP; +} + +static int nl_seq_check_handler(struct nl_msg *msg, void *arg) +{ + printf("%s\n", __func__); + + return NL_OK; +} + +static int handle_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]; + + genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL); + + if (genlhdr->cmd == THERMAL_GENL_EVENT_TZ_TRIP_HIGH) { + printf("THERMAL_GENL_EVENT_TZ_TRIP_HIGH\n"); + + if (attrs[THERMAL_GENL_ATTR_TZ_ID]) + printf("Thermal zone %d\n", + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID])); + + if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]) + printf("Thermal zone trip id %d\n", + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])); + } + + if (genlhdr->cmd == THERMAL_GENL_EVENT_TZ_TRIP_LOW) { + printf("THERMAL_GENL_EVENT_TZ_TRIP_LOW\n"); + + if (attrs[THERMAL_GENL_ATTR_TZ_ID]) + printf("Thermal zone %d\n", + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID])); + + if (attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]) + printf("Thermal zone trip id %d\n", + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID])); + } + + return 0; +} + +struct handler_args { + const char *group; + int id; +}; + +static int nl_send_msg(struct nl_sock *sock, struct nl_msg *msg, + int (*rx_handler)(struct nl_msg *, void *), + void *data) +{ + struct nl_cb *cb; + int err, done; + + cb = nl_cb_alloc(NL_CB_DEFAULT); + if (cb == NULL) + return -ENOMEM; + + err = nl_send_auto_complete(sock, msg); + if (err < 0) { + nl_cb_put(cb); + return err; + } + + err = done = 0; + 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); + + if (rx_handler != NULL) + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, rx_handler, data); + + while (err == 0 && done == 0) + nl_recvmsgs(sock, cb); + + nl_cb_put(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; +} + +static int nl_get_multicast_id(struct nl_sock *sock, const char *family, + const char *group) +{ + struct nl_msg *msg; + int err = 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); + + err = nl_send_msg(sock, msg, nl_family_handler, &grp); + if (err) + goto nla_put_failure; + + err = grp.id; + +nla_put_failure: + nlmsg_free(msg); + return err; +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sk_event; + int mcid; + + nl_debug = 0; + + /* Event receiving */ + sk_event = nl_socket_alloc(); + if (!sk_event) { + fprintf(stderr, "nl_socket_alloc failed\n"); + return -1; + } + + if (genl_connect(sk_event)) { + fprintf(stderr, "genl_connect(sk_event) failed\n"); + return -1; + } + + mcid = nl_get_multicast_id(sk_event, THERMAL_GENL_FAMILY_NAME, + THERMAL_GENL_EVENT_GROUP_NAME); + if (mcid < 0) { + fprintf(stderr, "nl_get_multicast_id failed\n"); + return -1; + } + + if (nl_socket_add_membership(sk_event, mcid)) { + fprintf(stderr, "nl_socket_add_membership failed"); + return -1; + } + + { + struct nl_cb *cb; + int err = 0, done = 0; + + cb = nl_cb_alloc(NL_CB_DEFAULT); + + 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_OVERRUN, NL_CB_CUSTOM, nl_overrun_handler, &done); + // nl_cb_set(cb, NL_CB_SKIPPED, NL_CB_CUSTOM, nl_skipped_handler, &done); + nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nl_seq_check_handler, &done); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, handle_event, NULL); + + while (!done && !err) + nl_recvmsgs(sk_event, cb); + } + + return 0; +} diff --git a/src/libthermal/thermal-genl.c b/src/libthermal/thermal-genl.c new file mode 100644 index 0000000..82966c2 --- /dev/null +++ b/src/libthermal/thermal-genl.c @@ -0,0 +1,534 @@ +#include <stdio.h> +#include <errno.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> + +#include "thermal.h" + +static int nl_error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) +{ + int *ret = arg; + + *ret = err->error; + + printf("%s\n", __func__); + + return NL_STOP; +} + +static int nl_finish_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + + *ret = 1; + + /* printf("%s\n", __func__); */ + + return NL_OK; +} + +static int nl_ack_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + + *ret = 1; + + printf("%s\n", __func__); + + return NL_OK; +} + +static int nl_overrun_handler(struct nl_msg *msg, void *arg) +{ + printf("%s\n", __func__); + + return NL_SKIP; +} + +static int nl_skipped_handler(struct nl_msg *msg, void *arg) +{ + printf("%s\n", __func__); + + return NL_SKIP; +} + +static int nl_seq_check_handler(struct nl_msg *msg, void *arg) +{ + printf("%s\n", __func__); + + return NL_OK; +} + +int show_tz(struct genl_info *info) +{ + struct nlattr *attr; + int rem; + + nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_TZ], rem) { + + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_ID) + printf("Thermal zone id: %d\n", nla_get_u32(attr)); + + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_NAME) + printf("Thermal zone name: %s\n", + nla_get_string(attr)); + } + + return 0; +} + +int show_cdev(struct genl_info *info) +{ + struct nlattr *attr; + int rem; + + nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_CDEV], rem) { + + if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_ID) + printf("Cooling device id: %d\n", nla_get_u32(attr)); + + if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_NAME) + printf("Thermal zone name: %s\n", nla_get_string(attr)); + } + + return 0; +} + +int show_trip(struct genl_info *info) +{ + struct nlattr *attr; + int rem; + + if (info->attrs[THERMAL_GENL_ATTR_TZ_TRIP]) + printf("Trip zone !!!\n"); + + nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_TZ_TRIP], rem) { + + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_NAME) + printf("Thermal zone name: %s\n", + nla_get_string(attr)); + + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_ID) + printf("Trip id: %d\n", nla_get_u32(attr)); + + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_TYPE) + printf("Trip type: %d\n", nla_get_u32(attr)); + + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_TEMP) + printf("Trip temp: %d\n", nla_get_u32(attr)); + + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_HYST) + printf("Trip hyst: %d\n", nla_get_u32(attr)); + } + + return 0; +} + +int show_temp(struct genl_info *info) +{ + if (info->attrs[THERMAL_GENL_ATTR_TZ_TEMP]) + printf("Thermal zone0 temperature: %d\n", + nla_get_u32(info->attrs[THERMAL_GENL_ATTR_TZ_TEMP])); + + return 0; +} + +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_MIN_STATE] = { .type = NLA_U32 }, + [THERMAL_GENL_ATTR_CDEV_NAME] = { .type = NLA_STRING }, +}; + +static int handle_sampling(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]; + + genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL); + + if (genlhdr->cmd == THERMAL_GENL_SAMPLING_TEMP) { + printf("THERMAL_GENL_SAMPLING_TEMP\n"); + + if (attrs[THERMAL_GENL_ATTR_TZ_ID]) + printf("Thermal zone %d\n", + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID])); + + if (attrs[THERMAL_GENL_ATTR_TZ_TEMP]) + printf("Thermal zone temp %d\n", + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP])); + } + + return 0; +} + +static int handle_netlink(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: + printf("THERMAL_GENL_CMD_TZ_GET\n"); + show_tz(info); + break; + + case THERMAL_GENL_CMD_CDEV_GET: + printf("THERMAL_GENL_CMD_CDEV_GET\n"); + show_cdev(info); + break; + + case THERMAL_GENL_CMD_TZ_GET_TEMP: + printf("THERMAL_GENL_CMD_TZ_GET_TEMP\n"); + show_temp(info); + break; + + case THERMAL_GENL_CMD_TZ_GET_TRIP: + printf("THERMAL_GENL_CMD_TZ_GET_TRIP\n"); + show_trip(info); + break; + }; + + 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_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 nl_sock *sock, int family, int id, int cmd, int flags) +{ + 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, family, 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_auto_complete(sock, msg) < 0) { + fprintf(stderr, "Failed to nl_send_auto_complete\n"); + return -1; + } + + { + struct nl_cb *cb; + int err = 0, done = 0; + + cb = nl_cb_alloc(NL_CB_DEFAULT); + + // 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_OVERRUN, NL_CB_CUSTOM, nl_overrun_handler, &done); + // nl_cb_set(cb, NL_CB_SKIPPED, NL_CB_CUSTOM, nl_skipped_handler, &done); + nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nl_seq_check_handler, &done); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, genl_handle_msg, NULL); + + while (!done && !err) + nl_recvmsgs(sock, cb); + } + + return 0; +} + +struct handler_args { + const char *group; + int id; +}; + +static int nl_send_msg(struct nl_sock *sock, struct nl_msg *msg, + int (*rx_handler)(struct nl_msg *, void *), + void *data) +{ + struct nl_cb *cb; + int err, done; + + cb = nl_cb_alloc(NL_CB_DEFAULT); + if (cb == NULL) + return -ENOMEM; + + err = nl_send_auto_complete(sock, msg); + if (err < 0) { + nl_cb_put(cb); + return err; + } + + err = done = 0; + 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); + + if (rx_handler != NULL) + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, rx_handler, data); + + while (err == 0 && done == 0) + nl_recvmsgs(sock, cb); + + nl_cb_put(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; +} + +static int nl_get_multicast_id(struct nl_sock *sock, const char *family, + const char *group) +{ + struct nl_msg *msg; + int err = 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); + + err = nl_send_msg(sock, msg, nl_family_handler, &grp); + if (err) + goto nla_put_failure; + + err = grp.id; + +nla_put_failure: + nlmsg_free(msg); + return err; +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sk_cmd; + struct nl_sock *sk_sample; + int ret, family; + int mcid; + + nl_debug = 0; + + sk_cmd = nl_socket_alloc(); + if (!sk_cmd) { + perror("nl_socket_alloc"); + return -1; + } + + nl_socket_modify_cb(sk_cmd, NL_CB_VALID, NL_CB_CUSTOM, + genl_handle_msg, NULL); + + ret = genl_connect(sk_cmd); + if (ret) { + fprintf(stderr, "genl_connect: %d\n", ret); + 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(sk_cmd, &thermal_cmd_ops); + if (ret) { + fprintf(stderr, "genl_ops_resolve: %d\n", ret); + return -1; + } + + family = genl_ctrl_resolve(sk_cmd, "nlctrl"); + if (family != GENL_ID_CTRL) { + fprintf(stderr, "genl_ctrl_resolve: %d\n", family); + return -1; + } + + goto listen; + + ret = thermal_genl_auto(sk_cmd, thermal_cmd_ops.o_id, -1, + THERMAL_GENL_CMD_TZ_GET, + NLM_F_DUMP | NLM_F_ACK); + if (ret < 0) { + fprintf(stderr, "Failed to thermal_genl_send: %d\n", ret); + return -1; + } + + ret = thermal_genl_auto(sk_cmd, thermal_cmd_ops.o_id, -1, + THERMAL_GENL_CMD_CDEV_GET, + NLM_F_DUMP | NLM_F_ACK); + if (ret < 0) { + fprintf(stderr, "Failed to thermal_genl_send: %d\n", ret); + return -1; + } + + ret = thermal_genl_auto(sk_cmd, thermal_cmd_ops.o_id, 1, + THERMAL_GENL_CMD_TZ_GET_TEMP, 0); + if (ret < 0) { + fprintf(stderr, "Failed to thermal_genl_send: %d\n", ret); + return -1; + } + + ret = thermal_genl_auto(sk_cmd, thermal_cmd_ops.o_id, 1, + THERMAL_GENL_CMD_TZ_GET_TRIP, 0); + if (ret < 0) { + fprintf(stderr, "Failed to thermal_genl_send: %d\n", ret); + return -1; + } +listen: + /* Sampling receiving */ + sk_sample = nl_socket_alloc(); + if (!sk_sample) { + fprintf(stderr, "nl_socket_alloc failed\n"); + return -1; + } + + if (genl_connect(sk_sample)) { + fprintf(stderr, "genl_connect(sk_sample) failed\n"); + return -1; + } + + mcid = nl_get_multicast_id(sk_sample, THERMAL_GENL_FAMILY_NAME, + THERMAL_GENL_SAMPLING_GROUP_NAME); + if (mcid < 0) { + fprintf(stderr, "nl_get_multicast_id failed\n"); + return -1; + } + + if (nl_socket_add_membership(sk_sample, mcid)) { + fprintf(stderr, "nl_socket_add_membership failed"); + return -1; + } + + { + struct nl_cb *cb; + int err = 0, done = 0; + + cb = nl_cb_alloc(NL_CB_DEFAULT); + + // 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_OVERRUN, NL_CB_CUSTOM, nl_overrun_handler, &done); + // nl_cb_set(cb, NL_CB_SKIPPED, NL_CB_CUSTOM, nl_skipped_handler, &done); + nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nl_seq_check_handler, &done); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, handle_sampling, NULL); + + while (!done && !err) + nl_recvmsgs(sk_sample, cb); + } + + return 0; +} diff --git a/src/libthermal/thermal-sampling.c b/src/libthermal/thermal-sampling.c new file mode 100644 index 0000000..4c18811 --- /dev/null +++ b/src/libthermal/thermal-sampling.c @@ -0,0 +1,249 @@ +#include <stdio.h> +#include <errno.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> + +#include "thermal.h" + +static int nl_error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) +{ + int *ret = arg; + + *ret = err->error; + + printf("%s\n", __func__); + + return NL_STOP; +} + +static int nl_finish_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + + *ret = 1; + + /* printf("%s\n", __func__); */ + + return NL_OK; +} + +static int nl_ack_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + + *ret = 1; + + printf("%s\n", __func__); + + return NL_OK; +} + +static int nl_overrun_handler(struct nl_msg *msg, void *arg) +{ + printf("%s\n", __func__); + + return NL_SKIP; +} + +static int nl_skipped_handler(struct nl_msg *msg, void *arg) +{ + printf("%s\n", __func__); + + return NL_SKIP; +} + +static int nl_seq_check_handler(struct nl_msg *msg, void *arg) +{ + printf("%s\n", __func__); + + return NL_OK; +} + +static int handle_sampling(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]; + + genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL); + + if (genlhdr->cmd == THERMAL_GENL_SAMPLING_TEMP) { + printf("THERMAL_GENL_SAMPLING_TEMP\n"); + + if (attrs[THERMAL_GENL_ATTR_TZ_ID]) + printf("Thermal zone %d\n", + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID])); + + if (attrs[THERMAL_GENL_ATTR_TZ_TEMP]) + printf("Thermal zone temp %d\n", + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP])); + } + + return 0; +} + +struct handler_args { + const char *group; + int id; +}; + +static int nl_send_msg(struct nl_sock *sock, struct nl_msg *msg, + int (*rx_handler)(struct nl_msg *, void *), + void *data) +{ + struct nl_cb *cb; + int err, done; + + cb = nl_cb_alloc(NL_CB_DEFAULT); + if (cb == NULL) + return -ENOMEM; + + err = nl_send_auto_complete(sock, msg); + if (err < 0) { + nl_cb_put(cb); + return err; + } + + err = done = 0; + 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); + + if (rx_handler != NULL) + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, rx_handler, data); + + while (err == 0 && done == 0) + nl_recvmsgs(sock, cb); + + nl_cb_put(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; +} + +static int nl_get_multicast_id(struct nl_sock *sock, const char *family, + const char *group) +{ + struct nl_msg *msg; + int err = 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); + + err = nl_send_msg(sock, msg, nl_family_handler, &grp); + if (err) + goto nla_put_failure; + + err = grp.id; + +nla_put_failure: + nlmsg_free(msg); + return err; +} + +int main(int argc, char *argv[]) +{ + struct nl_sock *sk_sample; + int mcid; + + nl_debug = 0; + + /* Sampling receiving */ + sk_sample = nl_socket_alloc(); + if (!sk_sample) { + fprintf(stderr, "nl_socket_alloc failed\n"); + return -1; + } + + if (genl_connect(sk_sample)) { + fprintf(stderr, "genl_connect(sk_sample) failed\n"); + return -1; + } + + mcid = nl_get_multicast_id(sk_sample, THERMAL_GENL_FAMILY_NAME, + THERMAL_GENL_SAMPLING_GROUP_NAME); + if (mcid < 0) { + fprintf(stderr, "nl_get_multicast_id failed\n"); + return -1; + } + + if (nl_socket_add_membership(sk_sample, mcid)) { + fprintf(stderr, "nl_socket_add_membership failed"); + return -1; + } + + { + struct nl_cb *cb; + int err = 0, done = 0; + + cb = nl_cb_alloc(NL_CB_DEFAULT); + + 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_OVERRUN, NL_CB_CUSTOM, nl_overrun_handler, &done); + // nl_cb_set(cb, NL_CB_SKIPPED, NL_CB_CUSTOM, nl_skipped_handler, &done); + nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nl_seq_check_handler, &done); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, handle_sampling, NULL); + + while (!done && !err) + nl_recvmsgs(sk_sample, cb); + } + + return 0; +} diff --git a/src/libthermal/thermal.c b/src/libthermal/thermal.c new file mode 100644 index 0000000..66dc009 --- /dev/null +++ b/src/libthermal/thermal.c @@ -0,0 +1,193 @@ +#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 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 (ops->tz_temp && thermal_sampling_init(th)) + goto out_free; + + return th; + +out_free: + free(th); + + return NULL; +} + +/*************************TEST POINT*****************************/ + +static int tz_create(const char *name, int tz_id) +{ + return 0; +} + +static int tz_delete(int tz_id) +{ + return 0; +} + +static int tz_disable(int tz_id) +{ + printf("Thermal zone %d disabled\n", tz_id); + + return 0; +} + +static int tz_enable(int tz_id) +{ + printf("Thermal zone %d enabled\n", tz_id); + + return 0; +} + +static int tz_temp(int tz_id, int temp) +{ + printf("Thermal zone %d temperature: %d\n", tz_id, temp); + return 0; +} + +static int trip_high(int tz_id, int trip_id, int temp) +{ + printf("Trip point %d crossed way up with %d °C\n", trip_id, temp); + return 0; +} + +static int trip_low(int tz_id, int trip_id, int temp) +{ + printf("Trip point %d crossed way down with %d °C\n", trip_id, temp); + return 0; +} + +static int trip_add(int tz_id, int trip_id, int type, int temp, int hyst) +{ + 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) +{ + 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) +{ + 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) +{ + return 0; +} + +static int cdev_delete(int cdev_id) +{ + return 0; +} + +static int cdev_update(int cdev_id, int cur_state) +{ + printf("cdev:%d state:%d\n", cdev_id, cur_state); + + return 0; +} + +static int gov_change(int tz_id, const char *name) +{ + printf("tz %d, governor=%s\n", tz_id, name); + + return 0; +} + +static struct thermal_ops ops = { + .tz_create = tz_create, + .tz_delete = tz_delete, + .tz_disable = tz_disable, + .tz_enable = tz_enable, + .tz_temp = tz_temp, + .trip_high = trip_high, + .trip_low = trip_low, + .trip_add = trip_add, + .trip_delete = trip_delete, + .trip_change = trip_change, + .cdev_add = cdev_add, + .cdev_delete = cdev_delete, + .cdev_update = cdev_update, + .gov_change = gov_change +}; + +#define MAX_EVENTS 10 + +int main(void) +{ + 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; + + 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); + } else if (events[i].data.ptr == thermal_sampling_handle) { + thermal_sampling_handle(th); + } + } + } + + return 0; +} diff --git a/src/libthermal/thermal.h b/src/libthermal/thermal.h new file mode 100644 index 0000000..bbbcb2c --- /dev/null +++ b/src/libthermal/thermal.h @@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __THERMAL_H +#define __THERMAL_H + +struct thermal_ops { + int (*tz_create)(const char *name, int tz_id); + int (*tz_delete)(int tz_id); + int (*tz_enable)(int tz_id); + int (*tz_disable)(int tz_id); + int (*tz_temp)(int tz_id, int temp); + int (*trip_high)(int tz_id, int trip_id, int temp); + int (*trip_low)(int tz_id, int trip_id, int temp); + int (*trip_add)(int tz_id, int trip_id, int type, int temp, int hyst); + int (*trip_change)(int tz_id, int trip_id, int type, int temp, int hyst); + int (*trip_delete)(int tz_id, int trip_id); + int (*cdev_add)(const char *name, int cdev_id, int max_state); + int (*cdev_delete)(int cdev_id); + int (*cdev_update)(int cdev_id, int cur_state); + int (*gov_change)(int tz_id, const char *gov_name); +}; + +struct thermal_handler { + struct thermal_ops *ops; + 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 *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); + +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_handle(struct thermal_handler *th); + +extern int thermal_cmd_fd(struct thermal_handler *th); + +/* + * Netlink thermal samples + */ +extern int thermal_sampling_init(struct thermal_handler *th); + +extern int thermal_sampling_handle(struct thermal_handler *th); + +extern int thermal_sampling_fd(struct thermal_handler *th); + +/* + * Low level netlink + */ + +extern int nl_subscribe_thermal(struct nl_sock *nl_sock, const char *group); + +extern int nl_thermal_connect(struct nl_sock **nl_sock, struct nl_cb **nl_cb, + int (*handler)(struct nl_msg *, void *), void *arg); + +extern void nl_thermal_disconnect(struct nl_sock *nl_sock, struct nl_cb *nl_cb); + +#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. + */ +#ifndef THERMAL_GENL_VERSION + +#warning "Thermal kernel/userspace API is not up-to-date, " \ + "please update the linux-headers" + +/* Adding event notification support elements */ +#undef THERMAL_GENL_FAMILY_NAME +#undef THERMAL_GENL_VERSION +#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) + +#endif |