11ce50e7dSDaniel Lezcano // SPDX-License-Identifier: GPL-2.0 21ce50e7dSDaniel Lezcano /* 31ce50e7dSDaniel Lezcano * Copyright 2020 Linaro Limited 41ce50e7dSDaniel Lezcano * 51ce50e7dSDaniel Lezcano * Author: Daniel Lezcano <daniel.lezcano@linaro.org> 61ce50e7dSDaniel Lezcano * 71ce50e7dSDaniel Lezcano * Generic netlink for thermal management framework 81ce50e7dSDaniel Lezcano */ 91ce50e7dSDaniel Lezcano #include <linux/module.h> 101ce50e7dSDaniel Lezcano #include <linux/kernel.h> 111ce50e7dSDaniel Lezcano #include <net/genetlink.h> 121ce50e7dSDaniel Lezcano #include <uapi/linux/thermal.h> 131ce50e7dSDaniel Lezcano 141ce50e7dSDaniel Lezcano #include "thermal_core.h" 151ce50e7dSDaniel Lezcano 161ce50e7dSDaniel Lezcano static const struct genl_multicast_group thermal_genl_mcgrps[] = { 171ce50e7dSDaniel Lezcano { .name = THERMAL_GENL_SAMPLING_GROUP_NAME, }, 181ce50e7dSDaniel Lezcano { .name = THERMAL_GENL_EVENT_GROUP_NAME, }, 191ce50e7dSDaniel Lezcano }; 201ce50e7dSDaniel Lezcano 211ce50e7dSDaniel Lezcano static const struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = { 221ce50e7dSDaniel Lezcano /* Thermal zone */ 231ce50e7dSDaniel Lezcano [THERMAL_GENL_ATTR_TZ] = { .type = NLA_NESTED }, 241ce50e7dSDaniel Lezcano [THERMAL_GENL_ATTR_TZ_ID] = { .type = NLA_U32 }, 251ce50e7dSDaniel Lezcano [THERMAL_GENL_ATTR_TZ_TEMP] = { .type = NLA_U32 }, 261ce50e7dSDaniel Lezcano [THERMAL_GENL_ATTR_TZ_TRIP] = { .type = NLA_NESTED }, 271ce50e7dSDaniel Lezcano [THERMAL_GENL_ATTR_TZ_TRIP_ID] = { .type = NLA_U32 }, 281ce50e7dSDaniel Lezcano [THERMAL_GENL_ATTR_TZ_TRIP_TEMP] = { .type = NLA_U32 }, 291ce50e7dSDaniel Lezcano [THERMAL_GENL_ATTR_TZ_TRIP_TYPE] = { .type = NLA_U32 }, 301ce50e7dSDaniel Lezcano [THERMAL_GENL_ATTR_TZ_TRIP_HYST] = { .type = NLA_U32 }, 311ce50e7dSDaniel Lezcano [THERMAL_GENL_ATTR_TZ_MODE] = { .type = NLA_U32 }, 321ce50e7dSDaniel Lezcano [THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT] = { .type = NLA_U32 }, 331ce50e7dSDaniel Lezcano [THERMAL_GENL_ATTR_TZ_NAME] = { .type = NLA_STRING, 341ce50e7dSDaniel Lezcano .len = THERMAL_NAME_LENGTH }, 351ce50e7dSDaniel Lezcano /* Governor(s) */ 361ce50e7dSDaniel Lezcano [THERMAL_GENL_ATTR_TZ_GOV] = { .type = NLA_NESTED }, 371ce50e7dSDaniel Lezcano [THERMAL_GENL_ATTR_TZ_GOV_NAME] = { .type = NLA_STRING, 381ce50e7dSDaniel Lezcano .len = THERMAL_NAME_LENGTH }, 391ce50e7dSDaniel Lezcano /* Cooling devices */ 401ce50e7dSDaniel Lezcano [THERMAL_GENL_ATTR_CDEV] = { .type = NLA_NESTED }, 411ce50e7dSDaniel Lezcano [THERMAL_GENL_ATTR_CDEV_ID] = { .type = NLA_U32 }, 421ce50e7dSDaniel Lezcano [THERMAL_GENL_ATTR_CDEV_CUR_STATE] = { .type = NLA_U32 }, 431ce50e7dSDaniel Lezcano [THERMAL_GENL_ATTR_CDEV_MAX_STATE] = { .type = NLA_U32 }, 441ce50e7dSDaniel Lezcano [THERMAL_GENL_ATTR_CDEV_NAME] = { .type = NLA_STRING, 451ce50e7dSDaniel Lezcano .len = THERMAL_NAME_LENGTH }, 46e4b1eb24SSrinivas Pandruvada /* CPU capabilities */ 47e4b1eb24SSrinivas Pandruvada [THERMAL_GENL_ATTR_CPU_CAPABILITY] = { .type = NLA_NESTED }, 48e4b1eb24SSrinivas Pandruvada [THERMAL_GENL_ATTR_CPU_CAPABILITY_ID] = { .type = NLA_U32 }, 49e4b1eb24SSrinivas Pandruvada [THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE] = { .type = NLA_U32 }, 50e4b1eb24SSrinivas Pandruvada [THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY] = { .type = NLA_U32 }, 511ce50e7dSDaniel Lezcano }; 521ce50e7dSDaniel Lezcano 531ce50e7dSDaniel Lezcano struct param { 541ce50e7dSDaniel Lezcano struct nlattr **attrs; 551ce50e7dSDaniel Lezcano struct sk_buff *msg; 561ce50e7dSDaniel Lezcano const char *name; 571ce50e7dSDaniel Lezcano int tz_id; 581ce50e7dSDaniel Lezcano int cdev_id; 591ce50e7dSDaniel Lezcano int trip_id; 601ce50e7dSDaniel Lezcano int trip_temp; 611ce50e7dSDaniel Lezcano int trip_type; 621ce50e7dSDaniel Lezcano int trip_hyst; 631ce50e7dSDaniel Lezcano int temp; 641ce50e7dSDaniel Lezcano int cdev_state; 651ce50e7dSDaniel Lezcano int cdev_max_state; 66e4b1eb24SSrinivas Pandruvada struct thermal_genl_cpu_caps *cpu_capabilities; 67e4b1eb24SSrinivas Pandruvada int cpu_capabilities_count; 681ce50e7dSDaniel Lezcano }; 691ce50e7dSDaniel Lezcano 701ce50e7dSDaniel Lezcano typedef int (*cb_t)(struct param *); 711ce50e7dSDaniel Lezcano 721ce50e7dSDaniel Lezcano static struct genl_family thermal_gnl_family; 731ce50e7dSDaniel Lezcano 741ce50e7dSDaniel Lezcano /************************** Sampling encoding *******************************/ 751ce50e7dSDaniel Lezcano 761ce50e7dSDaniel Lezcano int thermal_genl_sampling_temp(int id, int temp) 771ce50e7dSDaniel Lezcano { 781ce50e7dSDaniel Lezcano struct sk_buff *skb; 791ce50e7dSDaniel Lezcano void *hdr; 801ce50e7dSDaniel Lezcano 811ce50e7dSDaniel Lezcano skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 821ce50e7dSDaniel Lezcano if (!skb) 831ce50e7dSDaniel Lezcano return -ENOMEM; 841ce50e7dSDaniel Lezcano 851ce50e7dSDaniel Lezcano hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0, 861ce50e7dSDaniel Lezcano THERMAL_GENL_SAMPLING_TEMP); 871ce50e7dSDaniel Lezcano if (!hdr) 8848b45859SJing Xiangfeng goto out_free; 891ce50e7dSDaniel Lezcano 901ce50e7dSDaniel Lezcano if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_ID, id)) 911ce50e7dSDaniel Lezcano goto out_cancel; 921ce50e7dSDaniel Lezcano 931ce50e7dSDaniel Lezcano if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_TEMP, temp)) 941ce50e7dSDaniel Lezcano goto out_cancel; 951ce50e7dSDaniel Lezcano 961ce50e7dSDaniel Lezcano genlmsg_end(skb, hdr); 971ce50e7dSDaniel Lezcano 981ce50e7dSDaniel Lezcano genlmsg_multicast(&thermal_gnl_family, skb, 0, 0, GFP_KERNEL); 991ce50e7dSDaniel Lezcano 1001ce50e7dSDaniel Lezcano return 0; 1011ce50e7dSDaniel Lezcano out_cancel: 1021ce50e7dSDaniel Lezcano genlmsg_cancel(skb, hdr); 10348b45859SJing Xiangfeng out_free: 1041ce50e7dSDaniel Lezcano nlmsg_free(skb); 1051ce50e7dSDaniel Lezcano 1061ce50e7dSDaniel Lezcano return -EMSGSIZE; 1071ce50e7dSDaniel Lezcano } 1081ce50e7dSDaniel Lezcano 1091ce50e7dSDaniel Lezcano /**************************** Event encoding *********************************/ 1101ce50e7dSDaniel Lezcano 1111ce50e7dSDaniel Lezcano static int thermal_genl_event_tz_create(struct param *p) 1121ce50e7dSDaniel Lezcano { 1131ce50e7dSDaniel Lezcano if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) || 1141ce50e7dSDaniel Lezcano nla_put_string(p->msg, THERMAL_GENL_ATTR_TZ_NAME, p->name)) 1151ce50e7dSDaniel Lezcano return -EMSGSIZE; 1161ce50e7dSDaniel Lezcano 1171ce50e7dSDaniel Lezcano return 0; 1181ce50e7dSDaniel Lezcano } 1191ce50e7dSDaniel Lezcano 1201ce50e7dSDaniel Lezcano static int thermal_genl_event_tz(struct param *p) 1211ce50e7dSDaniel Lezcano { 1221ce50e7dSDaniel Lezcano if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id)) 1231ce50e7dSDaniel Lezcano return -EMSGSIZE; 1241ce50e7dSDaniel Lezcano 1251ce50e7dSDaniel Lezcano return 0; 1261ce50e7dSDaniel Lezcano } 1271ce50e7dSDaniel Lezcano 1281ce50e7dSDaniel Lezcano static int thermal_genl_event_tz_trip_up(struct param *p) 1291ce50e7dSDaniel Lezcano { 1301ce50e7dSDaniel Lezcano if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) || 131fc656fa1SDaniel Lezcano nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id) || 132fc656fa1SDaniel Lezcano nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TEMP, p->temp)) 1331ce50e7dSDaniel Lezcano return -EMSGSIZE; 1341ce50e7dSDaniel Lezcano 1351ce50e7dSDaniel Lezcano return 0; 1361ce50e7dSDaniel Lezcano } 1371ce50e7dSDaniel Lezcano 1381ce50e7dSDaniel Lezcano static int thermal_genl_event_tz_trip_add(struct param *p) 1391ce50e7dSDaniel Lezcano { 1401ce50e7dSDaniel Lezcano if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) || 1411ce50e7dSDaniel Lezcano nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id) || 1421ce50e7dSDaniel Lezcano nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, p->trip_type) || 1431ce50e7dSDaniel Lezcano nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, p->trip_temp) || 1441ce50e7dSDaniel Lezcano nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, p->trip_hyst)) 1451ce50e7dSDaniel Lezcano return -EMSGSIZE; 1461ce50e7dSDaniel Lezcano 1471ce50e7dSDaniel Lezcano return 0; 1481ce50e7dSDaniel Lezcano } 1491ce50e7dSDaniel Lezcano 1501ce50e7dSDaniel Lezcano static int thermal_genl_event_tz_trip_delete(struct param *p) 1511ce50e7dSDaniel Lezcano { 1521ce50e7dSDaniel Lezcano if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) || 1531ce50e7dSDaniel Lezcano nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id)) 1541ce50e7dSDaniel Lezcano return -EMSGSIZE; 1551ce50e7dSDaniel Lezcano 1561ce50e7dSDaniel Lezcano return 0; 1571ce50e7dSDaniel Lezcano } 1581ce50e7dSDaniel Lezcano 1591ce50e7dSDaniel Lezcano static int thermal_genl_event_cdev_add(struct param *p) 1601ce50e7dSDaniel Lezcano { 1611ce50e7dSDaniel Lezcano if (nla_put_string(p->msg, THERMAL_GENL_ATTR_CDEV_NAME, 1621ce50e7dSDaniel Lezcano p->name) || 1631ce50e7dSDaniel Lezcano nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID, 1641ce50e7dSDaniel Lezcano p->cdev_id) || 1651ce50e7dSDaniel Lezcano nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_MAX_STATE, 1661ce50e7dSDaniel Lezcano p->cdev_max_state)) 1671ce50e7dSDaniel Lezcano return -EMSGSIZE; 1681ce50e7dSDaniel Lezcano 1691ce50e7dSDaniel Lezcano return 0; 1701ce50e7dSDaniel Lezcano } 1711ce50e7dSDaniel Lezcano 1721ce50e7dSDaniel Lezcano static int thermal_genl_event_cdev_delete(struct param *p) 1731ce50e7dSDaniel Lezcano { 1741ce50e7dSDaniel Lezcano if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID, p->cdev_id)) 1751ce50e7dSDaniel Lezcano return -EMSGSIZE; 1761ce50e7dSDaniel Lezcano 1771ce50e7dSDaniel Lezcano return 0; 1781ce50e7dSDaniel Lezcano } 1791ce50e7dSDaniel Lezcano 1801ce50e7dSDaniel Lezcano static int thermal_genl_event_cdev_state_update(struct param *p) 1811ce50e7dSDaniel Lezcano { 1821ce50e7dSDaniel Lezcano if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID, 1831ce50e7dSDaniel Lezcano p->cdev_id) || 1841ce50e7dSDaniel Lezcano nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_CUR_STATE, 1851ce50e7dSDaniel Lezcano p->cdev_state)) 1861ce50e7dSDaniel Lezcano return -EMSGSIZE; 1871ce50e7dSDaniel Lezcano 1881ce50e7dSDaniel Lezcano return 0; 1891ce50e7dSDaniel Lezcano } 1901ce50e7dSDaniel Lezcano 1911ce50e7dSDaniel Lezcano static int thermal_genl_event_gov_change(struct param *p) 1921ce50e7dSDaniel Lezcano { 1931ce50e7dSDaniel Lezcano if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) || 1941ce50e7dSDaniel Lezcano nla_put_string(p->msg, THERMAL_GENL_ATTR_GOV_NAME, p->name)) 1951ce50e7dSDaniel Lezcano return -EMSGSIZE; 1961ce50e7dSDaniel Lezcano 1971ce50e7dSDaniel Lezcano return 0; 1981ce50e7dSDaniel Lezcano } 1991ce50e7dSDaniel Lezcano 200e4b1eb24SSrinivas Pandruvada static int thermal_genl_event_cpu_capability_change(struct param *p) 201e4b1eb24SSrinivas Pandruvada { 202e4b1eb24SSrinivas Pandruvada struct thermal_genl_cpu_caps *cpu_cap = p->cpu_capabilities; 203e4b1eb24SSrinivas Pandruvada struct sk_buff *msg = p->msg; 204e4b1eb24SSrinivas Pandruvada struct nlattr *start_cap; 205e4b1eb24SSrinivas Pandruvada int i; 206e4b1eb24SSrinivas Pandruvada 207e4b1eb24SSrinivas Pandruvada start_cap = nla_nest_start(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY); 208e4b1eb24SSrinivas Pandruvada if (!start_cap) 209e4b1eb24SSrinivas Pandruvada return -EMSGSIZE; 210e4b1eb24SSrinivas Pandruvada 211e4b1eb24SSrinivas Pandruvada for (i = 0; i < p->cpu_capabilities_count; ++i) { 212e4b1eb24SSrinivas Pandruvada if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_ID, 213e4b1eb24SSrinivas Pandruvada cpu_cap->cpu)) 214e4b1eb24SSrinivas Pandruvada goto out_cancel_nest; 215e4b1eb24SSrinivas Pandruvada 216e4b1eb24SSrinivas Pandruvada if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE, 217e4b1eb24SSrinivas Pandruvada cpu_cap->performance)) 218e4b1eb24SSrinivas Pandruvada goto out_cancel_nest; 219e4b1eb24SSrinivas Pandruvada 220e4b1eb24SSrinivas Pandruvada if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY, 221e4b1eb24SSrinivas Pandruvada cpu_cap->efficiency)) 222e4b1eb24SSrinivas Pandruvada goto out_cancel_nest; 223e4b1eb24SSrinivas Pandruvada 224e4b1eb24SSrinivas Pandruvada ++cpu_cap; 225e4b1eb24SSrinivas Pandruvada } 226e4b1eb24SSrinivas Pandruvada 227e4b1eb24SSrinivas Pandruvada nla_nest_end(msg, start_cap); 228e4b1eb24SSrinivas Pandruvada 229e4b1eb24SSrinivas Pandruvada return 0; 230e4b1eb24SSrinivas Pandruvada out_cancel_nest: 231e4b1eb24SSrinivas Pandruvada nla_nest_cancel(msg, start_cap); 232e4b1eb24SSrinivas Pandruvada 233e4b1eb24SSrinivas Pandruvada return -EMSGSIZE; 234e4b1eb24SSrinivas Pandruvada } 235e4b1eb24SSrinivas Pandruvada 2361ce50e7dSDaniel Lezcano int thermal_genl_event_tz_delete(struct param *p) 2371ce50e7dSDaniel Lezcano __attribute__((alias("thermal_genl_event_tz"))); 2381ce50e7dSDaniel Lezcano 2391ce50e7dSDaniel Lezcano int thermal_genl_event_tz_enable(struct param *p) 2401ce50e7dSDaniel Lezcano __attribute__((alias("thermal_genl_event_tz"))); 2411ce50e7dSDaniel Lezcano 2421ce50e7dSDaniel Lezcano int thermal_genl_event_tz_disable(struct param *p) 2431ce50e7dSDaniel Lezcano __attribute__((alias("thermal_genl_event_tz"))); 2441ce50e7dSDaniel Lezcano 2451ce50e7dSDaniel Lezcano int thermal_genl_event_tz_trip_down(struct param *p) 2461ce50e7dSDaniel Lezcano __attribute__((alias("thermal_genl_event_tz_trip_up"))); 2471ce50e7dSDaniel Lezcano 2481ce50e7dSDaniel Lezcano int thermal_genl_event_tz_trip_change(struct param *p) 2491ce50e7dSDaniel Lezcano __attribute__((alias("thermal_genl_event_tz_trip_add"))); 2501ce50e7dSDaniel Lezcano 2511ce50e7dSDaniel Lezcano static cb_t event_cb[] = { 2521ce50e7dSDaniel Lezcano [THERMAL_GENL_EVENT_TZ_CREATE] = thermal_genl_event_tz_create, 2531ce50e7dSDaniel Lezcano [THERMAL_GENL_EVENT_TZ_DELETE] = thermal_genl_event_tz_delete, 2541ce50e7dSDaniel Lezcano [THERMAL_GENL_EVENT_TZ_ENABLE] = thermal_genl_event_tz_enable, 2551ce50e7dSDaniel Lezcano [THERMAL_GENL_EVENT_TZ_DISABLE] = thermal_genl_event_tz_disable, 2561ce50e7dSDaniel Lezcano [THERMAL_GENL_EVENT_TZ_TRIP_UP] = thermal_genl_event_tz_trip_up, 2571ce50e7dSDaniel Lezcano [THERMAL_GENL_EVENT_TZ_TRIP_DOWN] = thermal_genl_event_tz_trip_down, 2581ce50e7dSDaniel Lezcano [THERMAL_GENL_EVENT_TZ_TRIP_CHANGE] = thermal_genl_event_tz_trip_change, 2591ce50e7dSDaniel Lezcano [THERMAL_GENL_EVENT_TZ_TRIP_ADD] = thermal_genl_event_tz_trip_add, 2601ce50e7dSDaniel Lezcano [THERMAL_GENL_EVENT_TZ_TRIP_DELETE] = thermal_genl_event_tz_trip_delete, 2611ce50e7dSDaniel Lezcano [THERMAL_GENL_EVENT_CDEV_ADD] = thermal_genl_event_cdev_add, 2621ce50e7dSDaniel Lezcano [THERMAL_GENL_EVENT_CDEV_DELETE] = thermal_genl_event_cdev_delete, 2631ce50e7dSDaniel Lezcano [THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = thermal_genl_event_cdev_state_update, 2641ce50e7dSDaniel Lezcano [THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = thermal_genl_event_gov_change, 265e4b1eb24SSrinivas Pandruvada [THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE] = thermal_genl_event_cpu_capability_change, 2661ce50e7dSDaniel Lezcano }; 2671ce50e7dSDaniel Lezcano 2681ce50e7dSDaniel Lezcano /* 2691ce50e7dSDaniel Lezcano * Generic netlink event encoding 2701ce50e7dSDaniel Lezcano */ 2711ce50e7dSDaniel Lezcano static int thermal_genl_send_event(enum thermal_genl_event event, 2721ce50e7dSDaniel Lezcano struct param *p) 2731ce50e7dSDaniel Lezcano { 2741ce50e7dSDaniel Lezcano struct sk_buff *msg; 2751ce50e7dSDaniel Lezcano int ret = -EMSGSIZE; 2761ce50e7dSDaniel Lezcano void *hdr; 2771ce50e7dSDaniel Lezcano 2781ce50e7dSDaniel Lezcano msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 2791ce50e7dSDaniel Lezcano if (!msg) 2801ce50e7dSDaniel Lezcano return -ENOMEM; 2811ce50e7dSDaniel Lezcano p->msg = msg; 2821ce50e7dSDaniel Lezcano 2831ce50e7dSDaniel Lezcano hdr = genlmsg_put(msg, 0, 0, &thermal_gnl_family, 0, event); 2841ce50e7dSDaniel Lezcano if (!hdr) 2851ce50e7dSDaniel Lezcano goto out_free_msg; 2861ce50e7dSDaniel Lezcano 2871ce50e7dSDaniel Lezcano ret = event_cb[event](p); 2881ce50e7dSDaniel Lezcano if (ret) 2891ce50e7dSDaniel Lezcano goto out_cancel_msg; 2901ce50e7dSDaniel Lezcano 2911ce50e7dSDaniel Lezcano genlmsg_end(msg, hdr); 2921ce50e7dSDaniel Lezcano 2931ce50e7dSDaniel Lezcano genlmsg_multicast(&thermal_gnl_family, msg, 0, 1, GFP_KERNEL); 2941ce50e7dSDaniel Lezcano 2951ce50e7dSDaniel Lezcano return 0; 2961ce50e7dSDaniel Lezcano 2971ce50e7dSDaniel Lezcano out_cancel_msg: 2981ce50e7dSDaniel Lezcano genlmsg_cancel(msg, hdr); 2991ce50e7dSDaniel Lezcano out_free_msg: 3001ce50e7dSDaniel Lezcano nlmsg_free(msg); 3011ce50e7dSDaniel Lezcano 3021ce50e7dSDaniel Lezcano return ret; 3031ce50e7dSDaniel Lezcano } 3041ce50e7dSDaniel Lezcano 3051ce50e7dSDaniel Lezcano int thermal_notify_tz_create(int tz_id, const char *name) 3061ce50e7dSDaniel Lezcano { 3071ce50e7dSDaniel Lezcano struct param p = { .tz_id = tz_id, .name = name }; 3081ce50e7dSDaniel Lezcano 3091ce50e7dSDaniel Lezcano return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_CREATE, &p); 3101ce50e7dSDaniel Lezcano } 3111ce50e7dSDaniel Lezcano 3121ce50e7dSDaniel Lezcano int thermal_notify_tz_delete(int tz_id) 3131ce50e7dSDaniel Lezcano { 3141ce50e7dSDaniel Lezcano struct param p = { .tz_id = tz_id }; 3151ce50e7dSDaniel Lezcano 3161ce50e7dSDaniel Lezcano return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DELETE, &p); 3171ce50e7dSDaniel Lezcano } 3181ce50e7dSDaniel Lezcano 3191ce50e7dSDaniel Lezcano int thermal_notify_tz_enable(int tz_id) 3201ce50e7dSDaniel Lezcano { 3211ce50e7dSDaniel Lezcano struct param p = { .tz_id = tz_id }; 3221ce50e7dSDaniel Lezcano 3231ce50e7dSDaniel Lezcano return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_ENABLE, &p); 3241ce50e7dSDaniel Lezcano } 3251ce50e7dSDaniel Lezcano 3261ce50e7dSDaniel Lezcano int thermal_notify_tz_disable(int tz_id) 3271ce50e7dSDaniel Lezcano { 3281ce50e7dSDaniel Lezcano struct param p = { .tz_id = tz_id }; 3291ce50e7dSDaniel Lezcano 3301ce50e7dSDaniel Lezcano return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DISABLE, &p); 3311ce50e7dSDaniel Lezcano } 3321ce50e7dSDaniel Lezcano 333fc656fa1SDaniel Lezcano int thermal_notify_tz_trip_down(int tz_id, int trip_id, int temp) 3341ce50e7dSDaniel Lezcano { 335fc656fa1SDaniel Lezcano struct param p = { .tz_id = tz_id, .trip_id = trip_id, .temp = temp }; 3361ce50e7dSDaniel Lezcano 3371ce50e7dSDaniel Lezcano return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DOWN, &p); 3381ce50e7dSDaniel Lezcano } 3391ce50e7dSDaniel Lezcano 340fc656fa1SDaniel Lezcano int thermal_notify_tz_trip_up(int tz_id, int trip_id, int temp) 3411ce50e7dSDaniel Lezcano { 342fc656fa1SDaniel Lezcano struct param p = { .tz_id = tz_id, .trip_id = trip_id, .temp = temp }; 3431ce50e7dSDaniel Lezcano 3441ce50e7dSDaniel Lezcano return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_UP, &p); 3451ce50e7dSDaniel Lezcano } 3461ce50e7dSDaniel Lezcano 3471ce50e7dSDaniel Lezcano int thermal_notify_tz_trip_add(int tz_id, int trip_id, int trip_type, 3481ce50e7dSDaniel Lezcano int trip_temp, int trip_hyst) 3491ce50e7dSDaniel Lezcano { 3501ce50e7dSDaniel Lezcano struct param p = { .tz_id = tz_id, .trip_id = trip_id, 3511ce50e7dSDaniel Lezcano .trip_type = trip_type, .trip_temp = trip_temp, 3521ce50e7dSDaniel Lezcano .trip_hyst = trip_hyst }; 3531ce50e7dSDaniel Lezcano 3541ce50e7dSDaniel Lezcano return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_ADD, &p); 3551ce50e7dSDaniel Lezcano } 3561ce50e7dSDaniel Lezcano 3571ce50e7dSDaniel Lezcano int thermal_notify_tz_trip_delete(int tz_id, int trip_id) 3581ce50e7dSDaniel Lezcano { 3591ce50e7dSDaniel Lezcano struct param p = { .tz_id = tz_id, .trip_id = trip_id }; 3601ce50e7dSDaniel Lezcano 3611ce50e7dSDaniel Lezcano return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DELETE, &p); 3621ce50e7dSDaniel Lezcano } 3631ce50e7dSDaniel Lezcano 3641ce50e7dSDaniel Lezcano int thermal_notify_tz_trip_change(int tz_id, int trip_id, int trip_type, 3651ce50e7dSDaniel Lezcano int trip_temp, int trip_hyst) 3661ce50e7dSDaniel Lezcano { 3671ce50e7dSDaniel Lezcano struct param p = { .tz_id = tz_id, .trip_id = trip_id, 3681ce50e7dSDaniel Lezcano .trip_type = trip_type, .trip_temp = trip_temp, 3691ce50e7dSDaniel Lezcano .trip_hyst = trip_hyst }; 3701ce50e7dSDaniel Lezcano 3711ce50e7dSDaniel Lezcano return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_CHANGE, &p); 3721ce50e7dSDaniel Lezcano } 3731ce50e7dSDaniel Lezcano 3741ce50e7dSDaniel Lezcano int thermal_notify_cdev_state_update(int cdev_id, int cdev_state) 3751ce50e7dSDaniel Lezcano { 3761ce50e7dSDaniel Lezcano struct param p = { .cdev_id = cdev_id, .cdev_state = cdev_state }; 3771ce50e7dSDaniel Lezcano 3781ce50e7dSDaniel Lezcano return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, &p); 3791ce50e7dSDaniel Lezcano } 3801ce50e7dSDaniel Lezcano 3811ce50e7dSDaniel Lezcano int thermal_notify_cdev_add(int cdev_id, const char *name, int cdev_max_state) 3821ce50e7dSDaniel Lezcano { 3831ce50e7dSDaniel Lezcano struct param p = { .cdev_id = cdev_id, .name = name, 3841ce50e7dSDaniel Lezcano .cdev_max_state = cdev_max_state }; 3851ce50e7dSDaniel Lezcano 3861ce50e7dSDaniel Lezcano return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_ADD, &p); 3871ce50e7dSDaniel Lezcano } 3881ce50e7dSDaniel Lezcano 3891ce50e7dSDaniel Lezcano int thermal_notify_cdev_delete(int cdev_id) 3901ce50e7dSDaniel Lezcano { 3911ce50e7dSDaniel Lezcano struct param p = { .cdev_id = cdev_id }; 3921ce50e7dSDaniel Lezcano 3931ce50e7dSDaniel Lezcano return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_DELETE, &p); 3941ce50e7dSDaniel Lezcano } 3951ce50e7dSDaniel Lezcano 3961ce50e7dSDaniel Lezcano int thermal_notify_tz_gov_change(int tz_id, const char *name) 3971ce50e7dSDaniel Lezcano { 3981ce50e7dSDaniel Lezcano struct param p = { .tz_id = tz_id, .name = name }; 3991ce50e7dSDaniel Lezcano 4001ce50e7dSDaniel Lezcano return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_GOV_CHANGE, &p); 4011ce50e7dSDaniel Lezcano } 4021ce50e7dSDaniel Lezcano 403e4b1eb24SSrinivas Pandruvada int thermal_genl_cpu_capability_event(int count, 404e4b1eb24SSrinivas Pandruvada struct thermal_genl_cpu_caps *caps) 405e4b1eb24SSrinivas Pandruvada { 406e4b1eb24SSrinivas Pandruvada struct param p = { .cpu_capabilities_count = count, .cpu_capabilities = caps }; 407e4b1eb24SSrinivas Pandruvada 408e4b1eb24SSrinivas Pandruvada return thermal_genl_send_event(THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE, &p); 409e4b1eb24SSrinivas Pandruvada } 410e4b1eb24SSrinivas Pandruvada EXPORT_SYMBOL_GPL(thermal_genl_cpu_capability_event); 411e4b1eb24SSrinivas Pandruvada 4121ce50e7dSDaniel Lezcano /*************************** Command encoding ********************************/ 4131ce50e7dSDaniel Lezcano 4141ce50e7dSDaniel Lezcano static int __thermal_genl_cmd_tz_get_id(struct thermal_zone_device *tz, 4151ce50e7dSDaniel Lezcano void *data) 4161ce50e7dSDaniel Lezcano { 4171ce50e7dSDaniel Lezcano struct sk_buff *msg = data; 4181ce50e7dSDaniel Lezcano 4191ce50e7dSDaniel Lezcano if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, tz->id) || 4201ce50e7dSDaniel Lezcano nla_put_string(msg, THERMAL_GENL_ATTR_TZ_NAME, tz->type)) 4211ce50e7dSDaniel Lezcano return -EMSGSIZE; 4221ce50e7dSDaniel Lezcano 4231ce50e7dSDaniel Lezcano return 0; 4241ce50e7dSDaniel Lezcano } 4251ce50e7dSDaniel Lezcano 4261ce50e7dSDaniel Lezcano static int thermal_genl_cmd_tz_get_id(struct param *p) 4271ce50e7dSDaniel Lezcano { 4281ce50e7dSDaniel Lezcano struct sk_buff *msg = p->msg; 4291ce50e7dSDaniel Lezcano struct nlattr *start_tz; 4301ce50e7dSDaniel Lezcano int ret; 4311ce50e7dSDaniel Lezcano 4321ce50e7dSDaniel Lezcano start_tz = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ); 4331ce50e7dSDaniel Lezcano if (!start_tz) 4341ce50e7dSDaniel Lezcano return -EMSGSIZE; 4351ce50e7dSDaniel Lezcano 4361ce50e7dSDaniel Lezcano ret = for_each_thermal_zone(__thermal_genl_cmd_tz_get_id, msg); 4371ce50e7dSDaniel Lezcano if (ret) 4381ce50e7dSDaniel Lezcano goto out_cancel_nest; 4391ce50e7dSDaniel Lezcano 4401ce50e7dSDaniel Lezcano nla_nest_end(msg, start_tz); 4411ce50e7dSDaniel Lezcano 4421ce50e7dSDaniel Lezcano return 0; 4431ce50e7dSDaniel Lezcano 4441ce50e7dSDaniel Lezcano out_cancel_nest: 4451ce50e7dSDaniel Lezcano nla_nest_cancel(msg, start_tz); 4461ce50e7dSDaniel Lezcano 4471ce50e7dSDaniel Lezcano return ret; 4481ce50e7dSDaniel Lezcano } 4491ce50e7dSDaniel Lezcano 4501ce50e7dSDaniel Lezcano static int thermal_genl_cmd_tz_get_trip(struct param *p) 4511ce50e7dSDaniel Lezcano { 4521ce50e7dSDaniel Lezcano struct sk_buff *msg = p->msg; 4531ce50e7dSDaniel Lezcano struct thermal_zone_device *tz; 4541ce50e7dSDaniel Lezcano struct nlattr *start_trip; 4551ce50e7dSDaniel Lezcano int i, id; 4561ce50e7dSDaniel Lezcano 4571ce50e7dSDaniel Lezcano if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID]) 4581ce50e7dSDaniel Lezcano return -EINVAL; 4591ce50e7dSDaniel Lezcano 4601ce50e7dSDaniel Lezcano id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]); 4611ce50e7dSDaniel Lezcano 4621ce50e7dSDaniel Lezcano tz = thermal_zone_get_by_id(id); 4631ce50e7dSDaniel Lezcano if (!tz) 4641ce50e7dSDaniel Lezcano return -EINVAL; 4651ce50e7dSDaniel Lezcano 4661ce50e7dSDaniel Lezcano start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ_TRIP); 4671ce50e7dSDaniel Lezcano if (!start_trip) 4681ce50e7dSDaniel Lezcano return -EMSGSIZE; 4691ce50e7dSDaniel Lezcano 4701ce50e7dSDaniel Lezcano mutex_lock(&tz->lock); 4711ce50e7dSDaniel Lezcano 472e5bfcd30SDaniel Lezcano for (i = 0; i < tz->num_trips; i++) { 4731ce50e7dSDaniel Lezcano 4741ce50e7dSDaniel Lezcano enum thermal_trip_type type; 4755838a148SNicolas Cavallari int temp, hyst = 0; 4761ce50e7dSDaniel Lezcano 4771ce50e7dSDaniel Lezcano tz->ops->get_trip_type(tz, i, &type); 4781ce50e7dSDaniel Lezcano tz->ops->get_trip_temp(tz, i, &temp); 4795838a148SNicolas Cavallari if (tz->ops->get_trip_hyst) 4801ce50e7dSDaniel Lezcano tz->ops->get_trip_hyst(tz, i, &hyst); 4811ce50e7dSDaniel Lezcano 4821ce50e7dSDaniel Lezcano if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, i) || 4831ce50e7dSDaniel Lezcano nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, type) || 4841ce50e7dSDaniel Lezcano nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, temp) || 4851ce50e7dSDaniel Lezcano nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, hyst)) 4861ce50e7dSDaniel Lezcano goto out_cancel_nest; 4871ce50e7dSDaniel Lezcano } 4881ce50e7dSDaniel Lezcano 4891ce50e7dSDaniel Lezcano mutex_unlock(&tz->lock); 4901ce50e7dSDaniel Lezcano 4911ce50e7dSDaniel Lezcano nla_nest_end(msg, start_trip); 4921ce50e7dSDaniel Lezcano 4931ce50e7dSDaniel Lezcano return 0; 4941ce50e7dSDaniel Lezcano 4951ce50e7dSDaniel Lezcano out_cancel_nest: 4961ce50e7dSDaniel Lezcano mutex_unlock(&tz->lock); 4971ce50e7dSDaniel Lezcano 4981ce50e7dSDaniel Lezcano return -EMSGSIZE; 4991ce50e7dSDaniel Lezcano } 5001ce50e7dSDaniel Lezcano 5011ce50e7dSDaniel Lezcano static int thermal_genl_cmd_tz_get_temp(struct param *p) 5021ce50e7dSDaniel Lezcano { 5031ce50e7dSDaniel Lezcano struct sk_buff *msg = p->msg; 5041ce50e7dSDaniel Lezcano struct thermal_zone_device *tz; 5051ce50e7dSDaniel Lezcano int temp, ret, id; 5061ce50e7dSDaniel Lezcano 5071ce50e7dSDaniel Lezcano if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID]) 5081ce50e7dSDaniel Lezcano return -EINVAL; 5091ce50e7dSDaniel Lezcano 5101ce50e7dSDaniel Lezcano id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]); 5111ce50e7dSDaniel Lezcano 5121ce50e7dSDaniel Lezcano tz = thermal_zone_get_by_id(id); 5131ce50e7dSDaniel Lezcano if (!tz) 5141ce50e7dSDaniel Lezcano return -EINVAL; 5151ce50e7dSDaniel Lezcano 5161ce50e7dSDaniel Lezcano ret = thermal_zone_get_temp(tz, &temp); 5171ce50e7dSDaniel Lezcano if (ret) 5181ce50e7dSDaniel Lezcano return ret; 5191ce50e7dSDaniel Lezcano 5201ce50e7dSDaniel Lezcano if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) || 5211ce50e7dSDaniel Lezcano nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TEMP, temp)) 5221ce50e7dSDaniel Lezcano return -EMSGSIZE; 5231ce50e7dSDaniel Lezcano 5241ce50e7dSDaniel Lezcano return 0; 5251ce50e7dSDaniel Lezcano } 5261ce50e7dSDaniel Lezcano 5271ce50e7dSDaniel Lezcano static int thermal_genl_cmd_tz_get_gov(struct param *p) 5281ce50e7dSDaniel Lezcano { 5291ce50e7dSDaniel Lezcano struct sk_buff *msg = p->msg; 5301ce50e7dSDaniel Lezcano struct thermal_zone_device *tz; 5311ce50e7dSDaniel Lezcano int id, ret = 0; 5321ce50e7dSDaniel Lezcano 5331ce50e7dSDaniel Lezcano if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID]) 5341ce50e7dSDaniel Lezcano return -EINVAL; 5351ce50e7dSDaniel Lezcano 5361ce50e7dSDaniel Lezcano id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]); 5371ce50e7dSDaniel Lezcano 5381ce50e7dSDaniel Lezcano tz = thermal_zone_get_by_id(id); 5391ce50e7dSDaniel Lezcano if (!tz) 5401ce50e7dSDaniel Lezcano return -EINVAL; 5411ce50e7dSDaniel Lezcano 5421ce50e7dSDaniel Lezcano mutex_lock(&tz->lock); 5431ce50e7dSDaniel Lezcano 5441ce50e7dSDaniel Lezcano if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) || 5451ce50e7dSDaniel Lezcano nla_put_string(msg, THERMAL_GENL_ATTR_TZ_GOV_NAME, 5461ce50e7dSDaniel Lezcano tz->governor->name)) 5471ce50e7dSDaniel Lezcano ret = -EMSGSIZE; 5481ce50e7dSDaniel Lezcano 5491ce50e7dSDaniel Lezcano mutex_unlock(&tz->lock); 5501ce50e7dSDaniel Lezcano 5511ce50e7dSDaniel Lezcano return ret; 5521ce50e7dSDaniel Lezcano } 5531ce50e7dSDaniel Lezcano 5541ce50e7dSDaniel Lezcano static int __thermal_genl_cmd_cdev_get(struct thermal_cooling_device *cdev, 5551ce50e7dSDaniel Lezcano void *data) 5561ce50e7dSDaniel Lezcano { 5571ce50e7dSDaniel Lezcano struct sk_buff *msg = data; 5581ce50e7dSDaniel Lezcano 5591ce50e7dSDaniel Lezcano if (nla_put_u32(msg, THERMAL_GENL_ATTR_CDEV_ID, cdev->id)) 5601ce50e7dSDaniel Lezcano return -EMSGSIZE; 5611ce50e7dSDaniel Lezcano 5621ce50e7dSDaniel Lezcano if (nla_put_string(msg, THERMAL_GENL_ATTR_CDEV_NAME, cdev->type)) 5631ce50e7dSDaniel Lezcano return -EMSGSIZE; 5641ce50e7dSDaniel Lezcano 5651ce50e7dSDaniel Lezcano return 0; 5661ce50e7dSDaniel Lezcano } 5671ce50e7dSDaniel Lezcano 5681ce50e7dSDaniel Lezcano static int thermal_genl_cmd_cdev_get(struct param *p) 5691ce50e7dSDaniel Lezcano { 5701ce50e7dSDaniel Lezcano struct sk_buff *msg = p->msg; 5711ce50e7dSDaniel Lezcano struct nlattr *start_cdev; 5721ce50e7dSDaniel Lezcano int ret; 5731ce50e7dSDaniel Lezcano 5741ce50e7dSDaniel Lezcano start_cdev = nla_nest_start(msg, THERMAL_GENL_ATTR_CDEV); 5751ce50e7dSDaniel Lezcano if (!start_cdev) 5761ce50e7dSDaniel Lezcano return -EMSGSIZE; 5771ce50e7dSDaniel Lezcano 5781ce50e7dSDaniel Lezcano ret = for_each_thermal_cooling_device(__thermal_genl_cmd_cdev_get, msg); 5791ce50e7dSDaniel Lezcano if (ret) 5801ce50e7dSDaniel Lezcano goto out_cancel_nest; 5811ce50e7dSDaniel Lezcano 5821ce50e7dSDaniel Lezcano nla_nest_end(msg, start_cdev); 5831ce50e7dSDaniel Lezcano 5841ce50e7dSDaniel Lezcano return 0; 5851ce50e7dSDaniel Lezcano out_cancel_nest: 5861ce50e7dSDaniel Lezcano nla_nest_cancel(msg, start_cdev); 5871ce50e7dSDaniel Lezcano 5881ce50e7dSDaniel Lezcano return ret; 5891ce50e7dSDaniel Lezcano } 5901ce50e7dSDaniel Lezcano 5911ce50e7dSDaniel Lezcano static cb_t cmd_cb[] = { 5921ce50e7dSDaniel Lezcano [THERMAL_GENL_CMD_TZ_GET_ID] = thermal_genl_cmd_tz_get_id, 5931ce50e7dSDaniel Lezcano [THERMAL_GENL_CMD_TZ_GET_TRIP] = thermal_genl_cmd_tz_get_trip, 5941ce50e7dSDaniel Lezcano [THERMAL_GENL_CMD_TZ_GET_TEMP] = thermal_genl_cmd_tz_get_temp, 5951ce50e7dSDaniel Lezcano [THERMAL_GENL_CMD_TZ_GET_GOV] = thermal_genl_cmd_tz_get_gov, 5961ce50e7dSDaniel Lezcano [THERMAL_GENL_CMD_CDEV_GET] = thermal_genl_cmd_cdev_get, 5971ce50e7dSDaniel Lezcano }; 5981ce50e7dSDaniel Lezcano 5991ce50e7dSDaniel Lezcano static int thermal_genl_cmd_dumpit(struct sk_buff *skb, 6001ce50e7dSDaniel Lezcano struct netlink_callback *cb) 6011ce50e7dSDaniel Lezcano { 6021ce50e7dSDaniel Lezcano struct param p = { .msg = skb }; 6031ce50e7dSDaniel Lezcano const struct genl_dumpit_info *info = genl_dumpit_info(cb); 6040b588afdSJakub Kicinski int cmd = info->op.cmd; 60552674f56SColin Ian King int ret; 6061ce50e7dSDaniel Lezcano void *hdr; 6071ce50e7dSDaniel Lezcano 6081ce50e7dSDaniel Lezcano hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0, cmd); 6091ce50e7dSDaniel Lezcano if (!hdr) 6101ce50e7dSDaniel Lezcano return -EMSGSIZE; 6111ce50e7dSDaniel Lezcano 6121ce50e7dSDaniel Lezcano ret = cmd_cb[cmd](&p); 6131ce50e7dSDaniel Lezcano if (ret) 6141ce50e7dSDaniel Lezcano goto out_cancel_msg; 6151ce50e7dSDaniel Lezcano 6161ce50e7dSDaniel Lezcano genlmsg_end(skb, hdr); 6171ce50e7dSDaniel Lezcano 6181ce50e7dSDaniel Lezcano return 0; 6191ce50e7dSDaniel Lezcano 6201ce50e7dSDaniel Lezcano out_cancel_msg: 6211ce50e7dSDaniel Lezcano genlmsg_cancel(skb, hdr); 6221ce50e7dSDaniel Lezcano 6231ce50e7dSDaniel Lezcano return ret; 6241ce50e7dSDaniel Lezcano } 6251ce50e7dSDaniel Lezcano 6261ce50e7dSDaniel Lezcano static int thermal_genl_cmd_doit(struct sk_buff *skb, 6271ce50e7dSDaniel Lezcano struct genl_info *info) 6281ce50e7dSDaniel Lezcano { 6291ce50e7dSDaniel Lezcano struct param p = { .attrs = info->attrs }; 6301ce50e7dSDaniel Lezcano struct sk_buff *msg; 6311ce50e7dSDaniel Lezcano void *hdr; 6321ce50e7dSDaniel Lezcano int cmd = info->genlhdr->cmd; 6331ce50e7dSDaniel Lezcano int ret = -EMSGSIZE; 6341ce50e7dSDaniel Lezcano 6351ce50e7dSDaniel Lezcano msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 6361ce50e7dSDaniel Lezcano if (!msg) 6371ce50e7dSDaniel Lezcano return -ENOMEM; 6381ce50e7dSDaniel Lezcano p.msg = msg; 6391ce50e7dSDaniel Lezcano 6401ce50e7dSDaniel Lezcano hdr = genlmsg_put_reply(msg, info, &thermal_gnl_family, 0, cmd); 6411ce50e7dSDaniel Lezcano if (!hdr) 6421ce50e7dSDaniel Lezcano goto out_free_msg; 6431ce50e7dSDaniel Lezcano 6441ce50e7dSDaniel Lezcano ret = cmd_cb[cmd](&p); 6451ce50e7dSDaniel Lezcano if (ret) 6461ce50e7dSDaniel Lezcano goto out_cancel_msg; 6471ce50e7dSDaniel Lezcano 6481ce50e7dSDaniel Lezcano genlmsg_end(msg, hdr); 6491ce50e7dSDaniel Lezcano 6501ce50e7dSDaniel Lezcano return genlmsg_reply(msg, info); 6511ce50e7dSDaniel Lezcano 6521ce50e7dSDaniel Lezcano out_cancel_msg: 6531ce50e7dSDaniel Lezcano genlmsg_cancel(msg, hdr); 6541ce50e7dSDaniel Lezcano out_free_msg: 6551ce50e7dSDaniel Lezcano nlmsg_free(msg); 6561ce50e7dSDaniel Lezcano 6571ce50e7dSDaniel Lezcano return ret; 6581ce50e7dSDaniel Lezcano } 6591ce50e7dSDaniel Lezcano 66066a9b928SJakub Kicinski static const struct genl_small_ops thermal_genl_ops[] = { 6611ce50e7dSDaniel Lezcano { 6621ce50e7dSDaniel Lezcano .cmd = THERMAL_GENL_CMD_TZ_GET_ID, 6631ce50e7dSDaniel Lezcano .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 6641ce50e7dSDaniel Lezcano .dumpit = thermal_genl_cmd_dumpit, 6651ce50e7dSDaniel Lezcano }, 6661ce50e7dSDaniel Lezcano { 6671ce50e7dSDaniel Lezcano .cmd = THERMAL_GENL_CMD_TZ_GET_TRIP, 6681ce50e7dSDaniel Lezcano .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 6691ce50e7dSDaniel Lezcano .doit = thermal_genl_cmd_doit, 6701ce50e7dSDaniel Lezcano }, 6711ce50e7dSDaniel Lezcano { 6721ce50e7dSDaniel Lezcano .cmd = THERMAL_GENL_CMD_TZ_GET_TEMP, 6731ce50e7dSDaniel Lezcano .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 6741ce50e7dSDaniel Lezcano .doit = thermal_genl_cmd_doit, 6751ce50e7dSDaniel Lezcano }, 6761ce50e7dSDaniel Lezcano { 6771ce50e7dSDaniel Lezcano .cmd = THERMAL_GENL_CMD_TZ_GET_GOV, 6781ce50e7dSDaniel Lezcano .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 6791ce50e7dSDaniel Lezcano .doit = thermal_genl_cmd_doit, 6801ce50e7dSDaniel Lezcano }, 6811ce50e7dSDaniel Lezcano { 6821ce50e7dSDaniel Lezcano .cmd = THERMAL_GENL_CMD_CDEV_GET, 6831ce50e7dSDaniel Lezcano .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 6841ce50e7dSDaniel Lezcano .dumpit = thermal_genl_cmd_dumpit, 6851ce50e7dSDaniel Lezcano }, 6861ce50e7dSDaniel Lezcano }; 6871ce50e7dSDaniel Lezcano 6881ce50e7dSDaniel Lezcano static struct genl_family thermal_gnl_family __ro_after_init = { 6891ce50e7dSDaniel Lezcano .hdrsize = 0, 6901ce50e7dSDaniel Lezcano .name = THERMAL_GENL_FAMILY_NAME, 6911ce50e7dSDaniel Lezcano .version = THERMAL_GENL_VERSION, 6921ce50e7dSDaniel Lezcano .maxattr = THERMAL_GENL_ATTR_MAX, 6931ce50e7dSDaniel Lezcano .policy = thermal_genl_policy, 69466a9b928SJakub Kicinski .small_ops = thermal_genl_ops, 69566a9b928SJakub Kicinski .n_small_ops = ARRAY_SIZE(thermal_genl_ops), 696*9c5d03d3SJakub Kicinski .resv_start_op = THERMAL_GENL_CMD_CDEV_GET + 1, 6971ce50e7dSDaniel Lezcano .mcgrps = thermal_genl_mcgrps, 6981ce50e7dSDaniel Lezcano .n_mcgrps = ARRAY_SIZE(thermal_genl_mcgrps), 6991ce50e7dSDaniel Lezcano }; 7001ce50e7dSDaniel Lezcano 701d2a89b52SDaniel Lezcano int __init thermal_netlink_init(void) 7021ce50e7dSDaniel Lezcano { 7031ce50e7dSDaniel Lezcano return genl_register_family(&thermal_gnl_family); 7041ce50e7dSDaniel Lezcano } 705