1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright 2020 Linaro Limited
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Generic netlink for thermal management framework
8*4882a593Smuzhiyun */
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/kernel.h>
11*4882a593Smuzhiyun #include <net/genetlink.h>
12*4882a593Smuzhiyun #include <trace/hooks/thermal.h>
13*4882a593Smuzhiyun #include <uapi/linux/thermal.h>
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #include "thermal_core.h"
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun static const struct genl_multicast_group thermal_genl_mcgrps[] = {
18*4882a593Smuzhiyun { .name = THERMAL_GENL_SAMPLING_GROUP_NAME, },
19*4882a593Smuzhiyun { .name = THERMAL_GENL_EVENT_GROUP_NAME, },
20*4882a593Smuzhiyun };
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun static const struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = {
23*4882a593Smuzhiyun /* Thermal zone */
24*4882a593Smuzhiyun [THERMAL_GENL_ATTR_TZ] = { .type = NLA_NESTED },
25*4882a593Smuzhiyun [THERMAL_GENL_ATTR_TZ_ID] = { .type = NLA_U32 },
26*4882a593Smuzhiyun [THERMAL_GENL_ATTR_TZ_TEMP] = { .type = NLA_U32 },
27*4882a593Smuzhiyun [THERMAL_GENL_ATTR_TZ_TRIP] = { .type = NLA_NESTED },
28*4882a593Smuzhiyun [THERMAL_GENL_ATTR_TZ_TRIP_ID] = { .type = NLA_U32 },
29*4882a593Smuzhiyun [THERMAL_GENL_ATTR_TZ_TRIP_TEMP] = { .type = NLA_U32 },
30*4882a593Smuzhiyun [THERMAL_GENL_ATTR_TZ_TRIP_TYPE] = { .type = NLA_U32 },
31*4882a593Smuzhiyun [THERMAL_GENL_ATTR_TZ_TRIP_HYST] = { .type = NLA_U32 },
32*4882a593Smuzhiyun [THERMAL_GENL_ATTR_TZ_MODE] = { .type = NLA_U32 },
33*4882a593Smuzhiyun [THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT] = { .type = NLA_U32 },
34*4882a593Smuzhiyun [THERMAL_GENL_ATTR_TZ_NAME] = { .type = NLA_STRING,
35*4882a593Smuzhiyun .len = THERMAL_NAME_LENGTH },
36*4882a593Smuzhiyun /* Governor(s) */
37*4882a593Smuzhiyun [THERMAL_GENL_ATTR_TZ_GOV] = { .type = NLA_NESTED },
38*4882a593Smuzhiyun [THERMAL_GENL_ATTR_TZ_GOV_NAME] = { .type = NLA_STRING,
39*4882a593Smuzhiyun .len = THERMAL_NAME_LENGTH },
40*4882a593Smuzhiyun /* Cooling devices */
41*4882a593Smuzhiyun [THERMAL_GENL_ATTR_CDEV] = { .type = NLA_NESTED },
42*4882a593Smuzhiyun [THERMAL_GENL_ATTR_CDEV_ID] = { .type = NLA_U32 },
43*4882a593Smuzhiyun [THERMAL_GENL_ATTR_CDEV_CUR_STATE] = { .type = NLA_U32 },
44*4882a593Smuzhiyun [THERMAL_GENL_ATTR_CDEV_MAX_STATE] = { .type = NLA_U32 },
45*4882a593Smuzhiyun [THERMAL_GENL_ATTR_CDEV_NAME] = { .type = NLA_STRING,
46*4882a593Smuzhiyun .len = THERMAL_NAME_LENGTH },
47*4882a593Smuzhiyun };
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun struct param {
50*4882a593Smuzhiyun struct nlattr **attrs;
51*4882a593Smuzhiyun struct sk_buff *msg;
52*4882a593Smuzhiyun const char *name;
53*4882a593Smuzhiyun int tz_id;
54*4882a593Smuzhiyun int cdev_id;
55*4882a593Smuzhiyun int trip_id;
56*4882a593Smuzhiyun int trip_temp;
57*4882a593Smuzhiyun int trip_type;
58*4882a593Smuzhiyun int trip_hyst;
59*4882a593Smuzhiyun int temp;
60*4882a593Smuzhiyun int cdev_state;
61*4882a593Smuzhiyun int cdev_max_state;
62*4882a593Smuzhiyun };
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun typedef int (*cb_t)(struct param *);
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun static struct genl_family thermal_gnl_family;
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun /************************** Sampling encoding *******************************/
69*4882a593Smuzhiyun
thermal_genl_sampling_temp(int id,int temp)70*4882a593Smuzhiyun int thermal_genl_sampling_temp(int id, int temp)
71*4882a593Smuzhiyun {
72*4882a593Smuzhiyun struct sk_buff *skb;
73*4882a593Smuzhiyun void *hdr;
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
76*4882a593Smuzhiyun if (!skb)
77*4882a593Smuzhiyun return -ENOMEM;
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0,
80*4882a593Smuzhiyun THERMAL_GENL_SAMPLING_TEMP);
81*4882a593Smuzhiyun if (!hdr)
82*4882a593Smuzhiyun goto out_free;
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_ID, id))
85*4882a593Smuzhiyun goto out_cancel;
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_TEMP, temp))
88*4882a593Smuzhiyun goto out_cancel;
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun genlmsg_end(skb, hdr);
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun genlmsg_multicast(&thermal_gnl_family, skb, 0, 0, GFP_KERNEL);
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun return 0;
95*4882a593Smuzhiyun out_cancel:
96*4882a593Smuzhiyun genlmsg_cancel(skb, hdr);
97*4882a593Smuzhiyun out_free:
98*4882a593Smuzhiyun nlmsg_free(skb);
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun return -EMSGSIZE;
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun /**************************** Event encoding *********************************/
104*4882a593Smuzhiyun
thermal_genl_event_tz_create(struct param * p)105*4882a593Smuzhiyun static int thermal_genl_event_tz_create(struct param *p)
106*4882a593Smuzhiyun {
107*4882a593Smuzhiyun if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
108*4882a593Smuzhiyun nla_put_string(p->msg, THERMAL_GENL_ATTR_TZ_NAME, p->name))
109*4882a593Smuzhiyun return -EMSGSIZE;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun return 0;
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun
thermal_genl_event_tz(struct param * p)114*4882a593Smuzhiyun static int thermal_genl_event_tz(struct param *p)
115*4882a593Smuzhiyun {
116*4882a593Smuzhiyun if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id))
117*4882a593Smuzhiyun return -EMSGSIZE;
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun return 0;
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun
thermal_genl_event_tz_trip_up(struct param * p)122*4882a593Smuzhiyun static int thermal_genl_event_tz_trip_up(struct param *p)
123*4882a593Smuzhiyun {
124*4882a593Smuzhiyun if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
125*4882a593Smuzhiyun nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id))
126*4882a593Smuzhiyun return -EMSGSIZE;
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun return 0;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun
thermal_genl_event_tz_trip_add(struct param * p)131*4882a593Smuzhiyun static int thermal_genl_event_tz_trip_add(struct param *p)
132*4882a593Smuzhiyun {
133*4882a593Smuzhiyun if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
134*4882a593Smuzhiyun nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id) ||
135*4882a593Smuzhiyun nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, p->trip_type) ||
136*4882a593Smuzhiyun nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, p->trip_temp) ||
137*4882a593Smuzhiyun nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, p->trip_hyst))
138*4882a593Smuzhiyun return -EMSGSIZE;
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun return 0;
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun
thermal_genl_event_tz_trip_delete(struct param * p)143*4882a593Smuzhiyun static int thermal_genl_event_tz_trip_delete(struct param *p)
144*4882a593Smuzhiyun {
145*4882a593Smuzhiyun if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
146*4882a593Smuzhiyun nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id))
147*4882a593Smuzhiyun return -EMSGSIZE;
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun return 0;
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun
thermal_genl_event_cdev_add(struct param * p)152*4882a593Smuzhiyun static int thermal_genl_event_cdev_add(struct param *p)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun if (nla_put_string(p->msg, THERMAL_GENL_ATTR_CDEV_NAME,
155*4882a593Smuzhiyun p->name) ||
156*4882a593Smuzhiyun nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID,
157*4882a593Smuzhiyun p->cdev_id) ||
158*4882a593Smuzhiyun nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_MAX_STATE,
159*4882a593Smuzhiyun p->cdev_max_state))
160*4882a593Smuzhiyun return -EMSGSIZE;
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun return 0;
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun
thermal_genl_event_cdev_delete(struct param * p)165*4882a593Smuzhiyun static int thermal_genl_event_cdev_delete(struct param *p)
166*4882a593Smuzhiyun {
167*4882a593Smuzhiyun if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID, p->cdev_id))
168*4882a593Smuzhiyun return -EMSGSIZE;
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun return 0;
171*4882a593Smuzhiyun }
172*4882a593Smuzhiyun
thermal_genl_event_cdev_state_update(struct param * p)173*4882a593Smuzhiyun static int thermal_genl_event_cdev_state_update(struct param *p)
174*4882a593Smuzhiyun {
175*4882a593Smuzhiyun if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID,
176*4882a593Smuzhiyun p->cdev_id) ||
177*4882a593Smuzhiyun nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_CUR_STATE,
178*4882a593Smuzhiyun p->cdev_state))
179*4882a593Smuzhiyun return -EMSGSIZE;
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun return 0;
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun
thermal_genl_event_gov_change(struct param * p)184*4882a593Smuzhiyun static int thermal_genl_event_gov_change(struct param *p)
185*4882a593Smuzhiyun {
186*4882a593Smuzhiyun if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
187*4882a593Smuzhiyun nla_put_string(p->msg, THERMAL_GENL_ATTR_GOV_NAME, p->name))
188*4882a593Smuzhiyun return -EMSGSIZE;
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun return 0;
191*4882a593Smuzhiyun }
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun int thermal_genl_event_tz_delete(struct param *p)
194*4882a593Smuzhiyun __attribute__((alias("thermal_genl_event_tz")));
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun int thermal_genl_event_tz_enable(struct param *p)
197*4882a593Smuzhiyun __attribute__((alias("thermal_genl_event_tz")));
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun int thermal_genl_event_tz_disable(struct param *p)
200*4882a593Smuzhiyun __attribute__((alias("thermal_genl_event_tz")));
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun int thermal_genl_event_tz_trip_down(struct param *p)
203*4882a593Smuzhiyun __attribute__((alias("thermal_genl_event_tz_trip_up")));
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun int thermal_genl_event_tz_trip_change(struct param *p)
206*4882a593Smuzhiyun __attribute__((alias("thermal_genl_event_tz_trip_add")));
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun static cb_t event_cb[] = {
209*4882a593Smuzhiyun [THERMAL_GENL_EVENT_TZ_CREATE] = thermal_genl_event_tz_create,
210*4882a593Smuzhiyun [THERMAL_GENL_EVENT_TZ_DELETE] = thermal_genl_event_tz_delete,
211*4882a593Smuzhiyun [THERMAL_GENL_EVENT_TZ_ENABLE] = thermal_genl_event_tz_enable,
212*4882a593Smuzhiyun [THERMAL_GENL_EVENT_TZ_DISABLE] = thermal_genl_event_tz_disable,
213*4882a593Smuzhiyun [THERMAL_GENL_EVENT_TZ_TRIP_UP] = thermal_genl_event_tz_trip_up,
214*4882a593Smuzhiyun [THERMAL_GENL_EVENT_TZ_TRIP_DOWN] = thermal_genl_event_tz_trip_down,
215*4882a593Smuzhiyun [THERMAL_GENL_EVENT_TZ_TRIP_CHANGE] = thermal_genl_event_tz_trip_change,
216*4882a593Smuzhiyun [THERMAL_GENL_EVENT_TZ_TRIP_ADD] = thermal_genl_event_tz_trip_add,
217*4882a593Smuzhiyun [THERMAL_GENL_EVENT_TZ_TRIP_DELETE] = thermal_genl_event_tz_trip_delete,
218*4882a593Smuzhiyun [THERMAL_GENL_EVENT_CDEV_ADD] = thermal_genl_event_cdev_add,
219*4882a593Smuzhiyun [THERMAL_GENL_EVENT_CDEV_DELETE] = thermal_genl_event_cdev_delete,
220*4882a593Smuzhiyun [THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = thermal_genl_event_cdev_state_update,
221*4882a593Smuzhiyun [THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = thermal_genl_event_gov_change,
222*4882a593Smuzhiyun };
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun /*
225*4882a593Smuzhiyun * Generic netlink event encoding
226*4882a593Smuzhiyun */
thermal_genl_send_event(enum thermal_genl_event event,struct param * p)227*4882a593Smuzhiyun static int thermal_genl_send_event(enum thermal_genl_event event,
228*4882a593Smuzhiyun struct param *p)
229*4882a593Smuzhiyun {
230*4882a593Smuzhiyun struct sk_buff *msg;
231*4882a593Smuzhiyun int ret = -EMSGSIZE;
232*4882a593Smuzhiyun void *hdr;
233*4882a593Smuzhiyun int enable_thermal_genl = 1;
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun trace_android_vh_enable_thermal_genl_check(event, p->tz_id, &enable_thermal_genl);
236*4882a593Smuzhiyun if (!enable_thermal_genl)
237*4882a593Smuzhiyun return 0;
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
240*4882a593Smuzhiyun if (!msg)
241*4882a593Smuzhiyun return -ENOMEM;
242*4882a593Smuzhiyun p->msg = msg;
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun hdr = genlmsg_put(msg, 0, 0, &thermal_gnl_family, 0, event);
245*4882a593Smuzhiyun if (!hdr)
246*4882a593Smuzhiyun goto out_free_msg;
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun ret = event_cb[event](p);
249*4882a593Smuzhiyun if (ret)
250*4882a593Smuzhiyun goto out_cancel_msg;
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun genlmsg_end(msg, hdr);
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun genlmsg_multicast(&thermal_gnl_family, msg, 0, 1, GFP_KERNEL);
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun return 0;
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun out_cancel_msg:
259*4882a593Smuzhiyun genlmsg_cancel(msg, hdr);
260*4882a593Smuzhiyun out_free_msg:
261*4882a593Smuzhiyun nlmsg_free(msg);
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun return ret;
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun
thermal_notify_tz_create(int tz_id,const char * name)266*4882a593Smuzhiyun int thermal_notify_tz_create(int tz_id, const char *name)
267*4882a593Smuzhiyun {
268*4882a593Smuzhiyun struct param p = { .tz_id = tz_id, .name = name };
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_CREATE, &p);
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun
thermal_notify_tz_delete(int tz_id)273*4882a593Smuzhiyun int thermal_notify_tz_delete(int tz_id)
274*4882a593Smuzhiyun {
275*4882a593Smuzhiyun struct param p = { .tz_id = tz_id };
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DELETE, &p);
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun
thermal_notify_tz_enable(int tz_id)280*4882a593Smuzhiyun int thermal_notify_tz_enable(int tz_id)
281*4882a593Smuzhiyun {
282*4882a593Smuzhiyun struct param p = { .tz_id = tz_id };
283*4882a593Smuzhiyun
284*4882a593Smuzhiyun return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_ENABLE, &p);
285*4882a593Smuzhiyun }
286*4882a593Smuzhiyun
thermal_notify_tz_disable(int tz_id)287*4882a593Smuzhiyun int thermal_notify_tz_disable(int tz_id)
288*4882a593Smuzhiyun {
289*4882a593Smuzhiyun struct param p = { .tz_id = tz_id };
290*4882a593Smuzhiyun
291*4882a593Smuzhiyun return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DISABLE, &p);
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun
thermal_notify_tz_trip_down(int tz_id,int trip_id)294*4882a593Smuzhiyun int thermal_notify_tz_trip_down(int tz_id, int trip_id)
295*4882a593Smuzhiyun {
296*4882a593Smuzhiyun struct param p = { .tz_id = tz_id, .trip_id = trip_id };
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DOWN, &p);
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun
thermal_notify_tz_trip_up(int tz_id,int trip_id)301*4882a593Smuzhiyun int thermal_notify_tz_trip_up(int tz_id, int trip_id)
302*4882a593Smuzhiyun {
303*4882a593Smuzhiyun struct param p = { .tz_id = tz_id, .trip_id = trip_id };
304*4882a593Smuzhiyun
305*4882a593Smuzhiyun return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_UP, &p);
306*4882a593Smuzhiyun }
307*4882a593Smuzhiyun
thermal_notify_tz_trip_add(int tz_id,int trip_id,int trip_type,int trip_temp,int trip_hyst)308*4882a593Smuzhiyun int thermal_notify_tz_trip_add(int tz_id, int trip_id, int trip_type,
309*4882a593Smuzhiyun int trip_temp, int trip_hyst)
310*4882a593Smuzhiyun {
311*4882a593Smuzhiyun struct param p = { .tz_id = tz_id, .trip_id = trip_id,
312*4882a593Smuzhiyun .trip_type = trip_type, .trip_temp = trip_temp,
313*4882a593Smuzhiyun .trip_hyst = trip_hyst };
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_ADD, &p);
316*4882a593Smuzhiyun }
317*4882a593Smuzhiyun
thermal_notify_tz_trip_delete(int tz_id,int trip_id)318*4882a593Smuzhiyun int thermal_notify_tz_trip_delete(int tz_id, int trip_id)
319*4882a593Smuzhiyun {
320*4882a593Smuzhiyun struct param p = { .tz_id = tz_id, .trip_id = trip_id };
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DELETE, &p);
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun
thermal_notify_tz_trip_change(int tz_id,int trip_id,int trip_type,int trip_temp,int trip_hyst)325*4882a593Smuzhiyun int thermal_notify_tz_trip_change(int tz_id, int trip_id, int trip_type,
326*4882a593Smuzhiyun int trip_temp, int trip_hyst)
327*4882a593Smuzhiyun {
328*4882a593Smuzhiyun struct param p = { .tz_id = tz_id, .trip_id = trip_id,
329*4882a593Smuzhiyun .trip_type = trip_type, .trip_temp = trip_temp,
330*4882a593Smuzhiyun .trip_hyst = trip_hyst };
331*4882a593Smuzhiyun
332*4882a593Smuzhiyun return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_CHANGE, &p);
333*4882a593Smuzhiyun }
334*4882a593Smuzhiyun
thermal_notify_cdev_state_update(int cdev_id,int cdev_state)335*4882a593Smuzhiyun int thermal_notify_cdev_state_update(int cdev_id, int cdev_state)
336*4882a593Smuzhiyun {
337*4882a593Smuzhiyun struct param p = { .cdev_id = cdev_id, .cdev_state = cdev_state };
338*4882a593Smuzhiyun
339*4882a593Smuzhiyun return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, &p);
340*4882a593Smuzhiyun }
341*4882a593Smuzhiyun
thermal_notify_cdev_add(int cdev_id,const char * name,int cdev_max_state)342*4882a593Smuzhiyun int thermal_notify_cdev_add(int cdev_id, const char *name, int cdev_max_state)
343*4882a593Smuzhiyun {
344*4882a593Smuzhiyun struct param p = { .cdev_id = cdev_id, .name = name,
345*4882a593Smuzhiyun .cdev_max_state = cdev_max_state };
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_ADD, &p);
348*4882a593Smuzhiyun }
349*4882a593Smuzhiyun
thermal_notify_cdev_delete(int cdev_id)350*4882a593Smuzhiyun int thermal_notify_cdev_delete(int cdev_id)
351*4882a593Smuzhiyun {
352*4882a593Smuzhiyun struct param p = { .cdev_id = cdev_id };
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_DELETE, &p);
355*4882a593Smuzhiyun }
356*4882a593Smuzhiyun
thermal_notify_tz_gov_change(int tz_id,const char * name)357*4882a593Smuzhiyun int thermal_notify_tz_gov_change(int tz_id, const char *name)
358*4882a593Smuzhiyun {
359*4882a593Smuzhiyun struct param p = { .tz_id = tz_id, .name = name };
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_GOV_CHANGE, &p);
362*4882a593Smuzhiyun }
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun /*************************** Command encoding ********************************/
365*4882a593Smuzhiyun
__thermal_genl_cmd_tz_get_id(struct thermal_zone_device * tz,void * data)366*4882a593Smuzhiyun static int __thermal_genl_cmd_tz_get_id(struct thermal_zone_device *tz,
367*4882a593Smuzhiyun void *data)
368*4882a593Smuzhiyun {
369*4882a593Smuzhiyun struct sk_buff *msg = data;
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, tz->id) ||
372*4882a593Smuzhiyun nla_put_string(msg, THERMAL_GENL_ATTR_TZ_NAME, tz->type))
373*4882a593Smuzhiyun return -EMSGSIZE;
374*4882a593Smuzhiyun
375*4882a593Smuzhiyun return 0;
376*4882a593Smuzhiyun }
377*4882a593Smuzhiyun
thermal_genl_cmd_tz_get_id(struct param * p)378*4882a593Smuzhiyun static int thermal_genl_cmd_tz_get_id(struct param *p)
379*4882a593Smuzhiyun {
380*4882a593Smuzhiyun struct sk_buff *msg = p->msg;
381*4882a593Smuzhiyun struct nlattr *start_tz;
382*4882a593Smuzhiyun int ret;
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun start_tz = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ);
385*4882a593Smuzhiyun if (!start_tz)
386*4882a593Smuzhiyun return -EMSGSIZE;
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun ret = for_each_thermal_zone(__thermal_genl_cmd_tz_get_id, msg);
389*4882a593Smuzhiyun if (ret)
390*4882a593Smuzhiyun goto out_cancel_nest;
391*4882a593Smuzhiyun
392*4882a593Smuzhiyun nla_nest_end(msg, start_tz);
393*4882a593Smuzhiyun
394*4882a593Smuzhiyun return 0;
395*4882a593Smuzhiyun
396*4882a593Smuzhiyun out_cancel_nest:
397*4882a593Smuzhiyun nla_nest_cancel(msg, start_tz);
398*4882a593Smuzhiyun
399*4882a593Smuzhiyun return ret;
400*4882a593Smuzhiyun }
401*4882a593Smuzhiyun
thermal_genl_cmd_tz_get_trip(struct param * p)402*4882a593Smuzhiyun static int thermal_genl_cmd_tz_get_trip(struct param *p)
403*4882a593Smuzhiyun {
404*4882a593Smuzhiyun struct sk_buff *msg = p->msg;
405*4882a593Smuzhiyun struct thermal_zone_device *tz;
406*4882a593Smuzhiyun struct nlattr *start_trip;
407*4882a593Smuzhiyun int i, id;
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
410*4882a593Smuzhiyun return -EINVAL;
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun tz = thermal_zone_get_by_id(id);
415*4882a593Smuzhiyun if (!tz)
416*4882a593Smuzhiyun return -EINVAL;
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ_TRIP);
419*4882a593Smuzhiyun if (!start_trip)
420*4882a593Smuzhiyun return -EMSGSIZE;
421*4882a593Smuzhiyun
422*4882a593Smuzhiyun mutex_lock(&tz->lock);
423*4882a593Smuzhiyun
424*4882a593Smuzhiyun for (i = 0; i < tz->trips; i++) {
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun enum thermal_trip_type type;
427*4882a593Smuzhiyun int temp, hyst = 0;
428*4882a593Smuzhiyun
429*4882a593Smuzhiyun tz->ops->get_trip_type(tz, i, &type);
430*4882a593Smuzhiyun tz->ops->get_trip_temp(tz, i, &temp);
431*4882a593Smuzhiyun if (tz->ops->get_trip_hyst)
432*4882a593Smuzhiyun tz->ops->get_trip_hyst(tz, i, &hyst);
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, i) ||
435*4882a593Smuzhiyun nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, type) ||
436*4882a593Smuzhiyun nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, temp) ||
437*4882a593Smuzhiyun nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, hyst))
438*4882a593Smuzhiyun goto out_cancel_nest;
439*4882a593Smuzhiyun }
440*4882a593Smuzhiyun
441*4882a593Smuzhiyun mutex_unlock(&tz->lock);
442*4882a593Smuzhiyun
443*4882a593Smuzhiyun nla_nest_end(msg, start_trip);
444*4882a593Smuzhiyun
445*4882a593Smuzhiyun return 0;
446*4882a593Smuzhiyun
447*4882a593Smuzhiyun out_cancel_nest:
448*4882a593Smuzhiyun mutex_unlock(&tz->lock);
449*4882a593Smuzhiyun
450*4882a593Smuzhiyun return -EMSGSIZE;
451*4882a593Smuzhiyun }
452*4882a593Smuzhiyun
thermal_genl_cmd_tz_get_temp(struct param * p)453*4882a593Smuzhiyun static int thermal_genl_cmd_tz_get_temp(struct param *p)
454*4882a593Smuzhiyun {
455*4882a593Smuzhiyun struct sk_buff *msg = p->msg;
456*4882a593Smuzhiyun struct thermal_zone_device *tz;
457*4882a593Smuzhiyun int temp, ret, id;
458*4882a593Smuzhiyun
459*4882a593Smuzhiyun if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
460*4882a593Smuzhiyun return -EINVAL;
461*4882a593Smuzhiyun
462*4882a593Smuzhiyun id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
463*4882a593Smuzhiyun
464*4882a593Smuzhiyun tz = thermal_zone_get_by_id(id);
465*4882a593Smuzhiyun if (!tz)
466*4882a593Smuzhiyun return -EINVAL;
467*4882a593Smuzhiyun
468*4882a593Smuzhiyun ret = thermal_zone_get_temp(tz, &temp);
469*4882a593Smuzhiyun if (ret)
470*4882a593Smuzhiyun return ret;
471*4882a593Smuzhiyun
472*4882a593Smuzhiyun if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
473*4882a593Smuzhiyun nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TEMP, temp))
474*4882a593Smuzhiyun return -EMSGSIZE;
475*4882a593Smuzhiyun
476*4882a593Smuzhiyun return 0;
477*4882a593Smuzhiyun }
478*4882a593Smuzhiyun
thermal_genl_cmd_tz_get_gov(struct param * p)479*4882a593Smuzhiyun static int thermal_genl_cmd_tz_get_gov(struct param *p)
480*4882a593Smuzhiyun {
481*4882a593Smuzhiyun struct sk_buff *msg = p->msg;
482*4882a593Smuzhiyun struct thermal_zone_device *tz;
483*4882a593Smuzhiyun int id, ret = 0;
484*4882a593Smuzhiyun
485*4882a593Smuzhiyun if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
486*4882a593Smuzhiyun return -EINVAL;
487*4882a593Smuzhiyun
488*4882a593Smuzhiyun id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun tz = thermal_zone_get_by_id(id);
491*4882a593Smuzhiyun if (!tz)
492*4882a593Smuzhiyun return -EINVAL;
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun mutex_lock(&tz->lock);
495*4882a593Smuzhiyun
496*4882a593Smuzhiyun if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
497*4882a593Smuzhiyun nla_put_string(msg, THERMAL_GENL_ATTR_TZ_GOV_NAME,
498*4882a593Smuzhiyun tz->governor->name))
499*4882a593Smuzhiyun ret = -EMSGSIZE;
500*4882a593Smuzhiyun
501*4882a593Smuzhiyun mutex_unlock(&tz->lock);
502*4882a593Smuzhiyun
503*4882a593Smuzhiyun return ret;
504*4882a593Smuzhiyun }
505*4882a593Smuzhiyun
__thermal_genl_cmd_cdev_get(struct thermal_cooling_device * cdev,void * data)506*4882a593Smuzhiyun static int __thermal_genl_cmd_cdev_get(struct thermal_cooling_device *cdev,
507*4882a593Smuzhiyun void *data)
508*4882a593Smuzhiyun {
509*4882a593Smuzhiyun struct sk_buff *msg = data;
510*4882a593Smuzhiyun
511*4882a593Smuzhiyun if (nla_put_u32(msg, THERMAL_GENL_ATTR_CDEV_ID, cdev->id))
512*4882a593Smuzhiyun return -EMSGSIZE;
513*4882a593Smuzhiyun
514*4882a593Smuzhiyun if (nla_put_string(msg, THERMAL_GENL_ATTR_CDEV_NAME, cdev->type))
515*4882a593Smuzhiyun return -EMSGSIZE;
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun return 0;
518*4882a593Smuzhiyun }
519*4882a593Smuzhiyun
thermal_genl_cmd_cdev_get(struct param * p)520*4882a593Smuzhiyun static int thermal_genl_cmd_cdev_get(struct param *p)
521*4882a593Smuzhiyun {
522*4882a593Smuzhiyun struct sk_buff *msg = p->msg;
523*4882a593Smuzhiyun struct nlattr *start_cdev;
524*4882a593Smuzhiyun int ret;
525*4882a593Smuzhiyun
526*4882a593Smuzhiyun start_cdev = nla_nest_start(msg, THERMAL_GENL_ATTR_CDEV);
527*4882a593Smuzhiyun if (!start_cdev)
528*4882a593Smuzhiyun return -EMSGSIZE;
529*4882a593Smuzhiyun
530*4882a593Smuzhiyun ret = for_each_thermal_cooling_device(__thermal_genl_cmd_cdev_get, msg);
531*4882a593Smuzhiyun if (ret)
532*4882a593Smuzhiyun goto out_cancel_nest;
533*4882a593Smuzhiyun
534*4882a593Smuzhiyun nla_nest_end(msg, start_cdev);
535*4882a593Smuzhiyun
536*4882a593Smuzhiyun return 0;
537*4882a593Smuzhiyun out_cancel_nest:
538*4882a593Smuzhiyun nla_nest_cancel(msg, start_cdev);
539*4882a593Smuzhiyun
540*4882a593Smuzhiyun return ret;
541*4882a593Smuzhiyun }
542*4882a593Smuzhiyun
543*4882a593Smuzhiyun static cb_t cmd_cb[] = {
544*4882a593Smuzhiyun [THERMAL_GENL_CMD_TZ_GET_ID] = thermal_genl_cmd_tz_get_id,
545*4882a593Smuzhiyun [THERMAL_GENL_CMD_TZ_GET_TRIP] = thermal_genl_cmd_tz_get_trip,
546*4882a593Smuzhiyun [THERMAL_GENL_CMD_TZ_GET_TEMP] = thermal_genl_cmd_tz_get_temp,
547*4882a593Smuzhiyun [THERMAL_GENL_CMD_TZ_GET_GOV] = thermal_genl_cmd_tz_get_gov,
548*4882a593Smuzhiyun [THERMAL_GENL_CMD_CDEV_GET] = thermal_genl_cmd_cdev_get,
549*4882a593Smuzhiyun };
550*4882a593Smuzhiyun
thermal_genl_cmd_dumpit(struct sk_buff * skb,struct netlink_callback * cb)551*4882a593Smuzhiyun static int thermal_genl_cmd_dumpit(struct sk_buff *skb,
552*4882a593Smuzhiyun struct netlink_callback *cb)
553*4882a593Smuzhiyun {
554*4882a593Smuzhiyun struct param p = { .msg = skb };
555*4882a593Smuzhiyun const struct genl_dumpit_info *info = genl_dumpit_info(cb);
556*4882a593Smuzhiyun int cmd = info->op.cmd;
557*4882a593Smuzhiyun int ret;
558*4882a593Smuzhiyun void *hdr;
559*4882a593Smuzhiyun
560*4882a593Smuzhiyun hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0, cmd);
561*4882a593Smuzhiyun if (!hdr)
562*4882a593Smuzhiyun return -EMSGSIZE;
563*4882a593Smuzhiyun
564*4882a593Smuzhiyun ret = cmd_cb[cmd](&p);
565*4882a593Smuzhiyun if (ret)
566*4882a593Smuzhiyun goto out_cancel_msg;
567*4882a593Smuzhiyun
568*4882a593Smuzhiyun genlmsg_end(skb, hdr);
569*4882a593Smuzhiyun
570*4882a593Smuzhiyun return 0;
571*4882a593Smuzhiyun
572*4882a593Smuzhiyun out_cancel_msg:
573*4882a593Smuzhiyun genlmsg_cancel(skb, hdr);
574*4882a593Smuzhiyun
575*4882a593Smuzhiyun return ret;
576*4882a593Smuzhiyun }
577*4882a593Smuzhiyun
thermal_genl_cmd_doit(struct sk_buff * skb,struct genl_info * info)578*4882a593Smuzhiyun static int thermal_genl_cmd_doit(struct sk_buff *skb,
579*4882a593Smuzhiyun struct genl_info *info)
580*4882a593Smuzhiyun {
581*4882a593Smuzhiyun struct param p = { .attrs = info->attrs };
582*4882a593Smuzhiyun struct sk_buff *msg;
583*4882a593Smuzhiyun void *hdr;
584*4882a593Smuzhiyun int cmd = info->genlhdr->cmd;
585*4882a593Smuzhiyun int ret = -EMSGSIZE;
586*4882a593Smuzhiyun
587*4882a593Smuzhiyun msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
588*4882a593Smuzhiyun if (!msg)
589*4882a593Smuzhiyun return -ENOMEM;
590*4882a593Smuzhiyun p.msg = msg;
591*4882a593Smuzhiyun
592*4882a593Smuzhiyun hdr = genlmsg_put_reply(msg, info, &thermal_gnl_family, 0, cmd);
593*4882a593Smuzhiyun if (!hdr)
594*4882a593Smuzhiyun goto out_free_msg;
595*4882a593Smuzhiyun
596*4882a593Smuzhiyun ret = cmd_cb[cmd](&p);
597*4882a593Smuzhiyun if (ret)
598*4882a593Smuzhiyun goto out_cancel_msg;
599*4882a593Smuzhiyun
600*4882a593Smuzhiyun genlmsg_end(msg, hdr);
601*4882a593Smuzhiyun
602*4882a593Smuzhiyun return genlmsg_reply(msg, info);
603*4882a593Smuzhiyun
604*4882a593Smuzhiyun out_cancel_msg:
605*4882a593Smuzhiyun genlmsg_cancel(msg, hdr);
606*4882a593Smuzhiyun out_free_msg:
607*4882a593Smuzhiyun nlmsg_free(msg);
608*4882a593Smuzhiyun
609*4882a593Smuzhiyun return ret;
610*4882a593Smuzhiyun }
611*4882a593Smuzhiyun
612*4882a593Smuzhiyun static const struct genl_small_ops thermal_genl_ops[] = {
613*4882a593Smuzhiyun {
614*4882a593Smuzhiyun .cmd = THERMAL_GENL_CMD_TZ_GET_ID,
615*4882a593Smuzhiyun .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
616*4882a593Smuzhiyun .dumpit = thermal_genl_cmd_dumpit,
617*4882a593Smuzhiyun },
618*4882a593Smuzhiyun {
619*4882a593Smuzhiyun .cmd = THERMAL_GENL_CMD_TZ_GET_TRIP,
620*4882a593Smuzhiyun .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
621*4882a593Smuzhiyun .doit = thermal_genl_cmd_doit,
622*4882a593Smuzhiyun },
623*4882a593Smuzhiyun {
624*4882a593Smuzhiyun .cmd = THERMAL_GENL_CMD_TZ_GET_TEMP,
625*4882a593Smuzhiyun .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
626*4882a593Smuzhiyun .doit = thermal_genl_cmd_doit,
627*4882a593Smuzhiyun },
628*4882a593Smuzhiyun {
629*4882a593Smuzhiyun .cmd = THERMAL_GENL_CMD_TZ_GET_GOV,
630*4882a593Smuzhiyun .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
631*4882a593Smuzhiyun .doit = thermal_genl_cmd_doit,
632*4882a593Smuzhiyun },
633*4882a593Smuzhiyun {
634*4882a593Smuzhiyun .cmd = THERMAL_GENL_CMD_CDEV_GET,
635*4882a593Smuzhiyun .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
636*4882a593Smuzhiyun .dumpit = thermal_genl_cmd_dumpit,
637*4882a593Smuzhiyun },
638*4882a593Smuzhiyun };
639*4882a593Smuzhiyun
640*4882a593Smuzhiyun static struct genl_family thermal_gnl_family __ro_after_init = {
641*4882a593Smuzhiyun .hdrsize = 0,
642*4882a593Smuzhiyun .name = THERMAL_GENL_FAMILY_NAME,
643*4882a593Smuzhiyun .version = THERMAL_GENL_VERSION,
644*4882a593Smuzhiyun .maxattr = THERMAL_GENL_ATTR_MAX,
645*4882a593Smuzhiyun .policy = thermal_genl_policy,
646*4882a593Smuzhiyun .small_ops = thermal_genl_ops,
647*4882a593Smuzhiyun .n_small_ops = ARRAY_SIZE(thermal_genl_ops),
648*4882a593Smuzhiyun .mcgrps = thermal_genl_mcgrps,
649*4882a593Smuzhiyun .n_mcgrps = ARRAY_SIZE(thermal_genl_mcgrps),
650*4882a593Smuzhiyun };
651*4882a593Smuzhiyun
thermal_netlink_init(void)652*4882a593Smuzhiyun int __init thermal_netlink_init(void)
653*4882a593Smuzhiyun {
654*4882a593Smuzhiyun return genl_register_family(&thermal_gnl_family);
655*4882a593Smuzhiyun }
656