1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
// SPDX-License-Identifier: LGPL-2.1+
// Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org>
#include <linux/netlink.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <thermal.h>
#include "thermal_nl.h"
/*
* Optimization: fill this array to tell which event we do want to pay
* attention to. That happens at init time with the ops
* structure. Each ops will enable the event and the general handler
* will be able to discard the event if there is not ops associated
* with it.
*/
static int enabled_ops[__THERMAL_GENL_EVENT_MAX];
static int handle_thermal_event(struct nl_msg *n, void *arg)
{
struct nlmsghdr *nlh = nlmsg_hdr(n);
struct genlmsghdr *genlhdr = genlmsg_hdr(nlh);
struct nlattr *attrs[THERMAL_GENL_ATTR_MAX + 1];
struct thermal_handler_param *thp = arg;
struct thermal_events_ops *ops = &thp->th->ops->events;
genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL);
arg = thp->arg;
/*
* This is an event we don't care of, bail out.
*/
if (!enabled_ops[genlhdr->cmd])
return THERMAL_SUCCESS;
switch (genlhdr->cmd) {
case THERMAL_GENL_EVENT_TZ_CREATE:
return ops->tz_create(nla_get_string(attrs[THERMAL_GENL_ATTR_TZ_NAME]),
nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg);
case THERMAL_GENL_EVENT_TZ_DELETE:
return ops->tz_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg);
case THERMAL_GENL_EVENT_TZ_ENABLE:
return ops->tz_enable(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg);
case THERMAL_GENL_EVENT_TZ_DISABLE:
return ops->tz_disable(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg);
case THERMAL_GENL_EVENT_TZ_TRIP_CHANGE:
return ops->trip_change(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]),
nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]),
nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]),
nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]),
nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]), arg);
case THERMAL_GENL_EVENT_TZ_TRIP_ADD:
return ops->trip_add(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]),
nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]),
nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]),
nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]),
nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]), arg);
case THERMAL_GENL_EVENT_TZ_TRIP_DELETE:
return ops->trip_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]),
nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]), arg);
case THERMAL_GENL_EVENT_TZ_TRIP_UP:
return 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]), arg);
case THERMAL_GENL_EVENT_TZ_TRIP_DOWN:
return 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]), arg);
case THERMAL_GENL_EVENT_CDEV_ADD:
return ops->cdev_add(nla_get_string(attrs[THERMAL_GENL_ATTR_CDEV_NAME]),
nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]),
nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_MAX_STATE]), arg);
case THERMAL_GENL_EVENT_CDEV_DELETE:
return ops->cdev_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]), arg);
case THERMAL_GENL_EVENT_CDEV_STATE_UPDATE:
return ops->cdev_update(nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]),
nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_CUR_STATE]), arg);
case THERMAL_GENL_EVENT_TZ_GOV_CHANGE:
return ops->gov_change(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]),
nla_get_string(attrs[THERMAL_GENL_ATTR_GOV_NAME]), arg);
default:
return -1;
}
}
static void thermal_events_ops_init(struct thermal_events_ops *ops)
{
enabled_ops[THERMAL_GENL_EVENT_TZ_CREATE] = !!ops->tz_create;
enabled_ops[THERMAL_GENL_EVENT_TZ_DELETE] = !!ops->tz_delete;
enabled_ops[THERMAL_GENL_EVENT_TZ_DISABLE] = !!ops->tz_disable;
enabled_ops[THERMAL_GENL_EVENT_TZ_ENABLE] = !!ops->tz_enable;
enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_UP] = !!ops->trip_high;
enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DOWN] = !!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_CDEV_ADD] = !!ops->cdev_add;
enabled_ops[THERMAL_GENL_EVENT_CDEV_DELETE] = !!ops->cdev_delete;
enabled_ops[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = !!ops->cdev_update;
enabled_ops[THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = !!ops->gov_change;
}
thermal_error_t thermal_events_handle(struct thermal_handler *th, void *arg)
{
struct thermal_handler_param thp = { .th = th, .arg = arg };
if (!th)
return THERMAL_ERROR;
if (nl_cb_set(th->cb_event, NL_CB_VALID, NL_CB_CUSTOM,
handle_thermal_event, &thp))
return THERMAL_ERROR;
return nl_recvmsgs(th->sk_event, th->cb_event);
}
int thermal_events_fd(struct thermal_handler *th)
{
if (!th)
return -1;
return nl_socket_get_fd(th->sk_event);
}
thermal_error_t thermal_events_exit(struct thermal_handler *th)
{
if (nl_unsubscribe_thermal(th->sk_event, th->cb_event,
THERMAL_GENL_EVENT_GROUP_NAME))
return THERMAL_ERROR;
nl_thermal_disconnect(th->sk_event, th->cb_event);
return THERMAL_SUCCESS;
}
thermal_error_t thermal_events_init(struct thermal_handler *th)
{
thermal_events_ops_init(&th->ops->events);
if (nl_thermal_connect(&th->sk_event, &th->cb_event))
return THERMAL_ERROR;
if (nl_subscribe_thermal(th->sk_event, th->cb_event,
THERMAL_GENL_EVENT_GROUP_NAME))
return THERMAL_ERROR;
return THERMAL_SUCCESS;
}
|