Linux Audio

Check our new training course

Loading...
v6.2
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright 2020 Linaro Limited
  4 *
  5 * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
  6 *
  7 * Generic netlink for thermal management framework
  8 */
  9#include <linux/module.h>
 
 10#include <linux/kernel.h>
 
 11#include <net/genetlink.h>
 12#include <uapi/linux/thermal.h>
 13
 14#include "thermal_core.h"
 15
 16static const struct genl_multicast_group thermal_genl_mcgrps[] = {
 17	{ .name = THERMAL_GENL_SAMPLING_GROUP_NAME, },
 18	{ .name = THERMAL_GENL_EVENT_GROUP_NAME,  },
 19};
 20
 21static const struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = {
 22	/* Thermal zone */
 23	[THERMAL_GENL_ATTR_TZ]			= { .type = NLA_NESTED },
 24	[THERMAL_GENL_ATTR_TZ_ID]		= { .type = NLA_U32 },
 25	[THERMAL_GENL_ATTR_TZ_TEMP]		= { .type = NLA_U32 },
 26	[THERMAL_GENL_ATTR_TZ_TRIP]		= { .type = NLA_NESTED },
 27	[THERMAL_GENL_ATTR_TZ_TRIP_ID]		= { .type = NLA_U32 },
 28	[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]	= { .type = NLA_U32 },
 29	[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]	= { .type = NLA_U32 },
 30	[THERMAL_GENL_ATTR_TZ_TRIP_HYST]	= { .type = NLA_U32 },
 31	[THERMAL_GENL_ATTR_TZ_MODE]		= { .type = NLA_U32 },
 32	[THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT]	= { .type = NLA_U32 },
 33	[THERMAL_GENL_ATTR_TZ_NAME]		= { .type = NLA_STRING,
 34						    .len = THERMAL_NAME_LENGTH },
 35	/* Governor(s) */
 36	[THERMAL_GENL_ATTR_TZ_GOV]		= { .type = NLA_NESTED },
 37	[THERMAL_GENL_ATTR_TZ_GOV_NAME]		= { .type = NLA_STRING,
 38						    .len = THERMAL_NAME_LENGTH },
 39	/* Cooling devices */
 40	[THERMAL_GENL_ATTR_CDEV]		= { .type = NLA_NESTED },
 41	[THERMAL_GENL_ATTR_CDEV_ID]		= { .type = NLA_U32 },
 42	[THERMAL_GENL_ATTR_CDEV_CUR_STATE]	= { .type = NLA_U32 },
 43	[THERMAL_GENL_ATTR_CDEV_MAX_STATE]	= { .type = NLA_U32 },
 44	[THERMAL_GENL_ATTR_CDEV_NAME]		= { .type = NLA_STRING,
 45						    .len = THERMAL_NAME_LENGTH },
 46	/* CPU capabilities */
 47	[THERMAL_GENL_ATTR_CPU_CAPABILITY]		= { .type = NLA_NESTED },
 48	[THERMAL_GENL_ATTR_CPU_CAPABILITY_ID]		= { .type = NLA_U32 },
 49	[THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE]	= { .type = NLA_U32 },
 50	[THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY]	= { .type = NLA_U32 },
 
 
 
 
 
 51};
 52
 53struct param {
 54	struct nlattr **attrs;
 55	struct sk_buff *msg;
 56	const char *name;
 57	int tz_id;
 58	int cdev_id;
 59	int trip_id;
 60	int trip_temp;
 61	int trip_type;
 62	int trip_hyst;
 63	int temp;
 
 
 64	int cdev_state;
 65	int cdev_max_state;
 66	struct thermal_genl_cpu_caps *cpu_capabilities;
 67	int cpu_capabilities_count;
 68};
 69
 70typedef int (*cb_t)(struct param *);
 71
 72static struct genl_family thermal_gnl_family;
 
 
 
 
 
 
 73
 74/************************** Sampling encoding *******************************/
 75
 76int thermal_genl_sampling_temp(int id, int temp)
 77{
 78	struct sk_buff *skb;
 79	void *hdr;
 80
 
 
 
 81	skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
 82	if (!skb)
 83		return -ENOMEM;
 84
 85	hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0,
 86			  THERMAL_GENL_SAMPLING_TEMP);
 87	if (!hdr)
 88		goto out_free;
 89
 90	if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_ID, id))
 91		goto out_cancel;
 92
 93	if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_TEMP, temp))
 94		goto out_cancel;
 95
 96	genlmsg_end(skb, hdr);
 97
 98	genlmsg_multicast(&thermal_gnl_family, skb, 0, 0, GFP_KERNEL);
 99
100	return 0;
101out_cancel:
102	genlmsg_cancel(skb, hdr);
103out_free:
104	nlmsg_free(skb);
105
106	return -EMSGSIZE;
107}
108
109/**************************** Event encoding *********************************/
110
111static int thermal_genl_event_tz_create(struct param *p)
112{
113	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
114	    nla_put_string(p->msg, THERMAL_GENL_ATTR_TZ_NAME, p->name))
115		return -EMSGSIZE;
116
117	return 0;
118}
119
120static int thermal_genl_event_tz(struct param *p)
121{
122	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id))
123		return -EMSGSIZE;
124
125	return 0;
126}
127
128static int thermal_genl_event_tz_trip_up(struct param *p)
129{
130	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
131	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id) ||
132	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TEMP, p->temp))
133		return -EMSGSIZE;
134
135	return 0;
136}
137
138static int thermal_genl_event_tz_trip_add(struct param *p)
139{
140	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
141	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id) ||
142	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, p->trip_type) ||
143	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, p->trip_temp) ||
144	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, p->trip_hyst))
145		return -EMSGSIZE;
146
147	return 0;
148}
149
150static int thermal_genl_event_tz_trip_delete(struct param *p)
151{
152	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
153	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id))
154		return -EMSGSIZE;
155
156	return 0;
157}
158
159static int thermal_genl_event_cdev_add(struct param *p)
160{
161	if (nla_put_string(p->msg, THERMAL_GENL_ATTR_CDEV_NAME,
162			   p->name) ||
163	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID,
164			p->cdev_id) ||
165	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_MAX_STATE,
166			p->cdev_max_state))
167		return -EMSGSIZE;
168
169	return 0;
170}
171
172static int thermal_genl_event_cdev_delete(struct param *p)
173{
174	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID, p->cdev_id))
175		return -EMSGSIZE;
176
177	return 0;
178}
179
180static int thermal_genl_event_cdev_state_update(struct param *p)
181{
182	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID,
183			p->cdev_id) ||
184	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_CUR_STATE,
185			p->cdev_state))
186		return -EMSGSIZE;
187
188	return 0;
189}
190
191static int thermal_genl_event_gov_change(struct param *p)
192{
193	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
194	    nla_put_string(p->msg, THERMAL_GENL_ATTR_GOV_NAME, p->name))
195		return -EMSGSIZE;
196
197	return 0;
198}
199
200static int thermal_genl_event_cpu_capability_change(struct param *p)
201{
202	struct thermal_genl_cpu_caps *cpu_cap = p->cpu_capabilities;
203	struct sk_buff *msg = p->msg;
204	struct nlattr *start_cap;
205	int i;
206
207	start_cap = nla_nest_start(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY);
208	if (!start_cap)
209		return -EMSGSIZE;
210
211	for (i = 0; i < p->cpu_capabilities_count; ++i) {
212		if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_ID,
213				cpu_cap->cpu))
214			goto out_cancel_nest;
215
216		if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE,
217				cpu_cap->performance))
218			goto out_cancel_nest;
219
220		if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY,
221				cpu_cap->efficiency))
222			goto out_cancel_nest;
223
224		++cpu_cap;
225	}
226
227	nla_nest_end(msg, start_cap);
228
229	return 0;
230out_cancel_nest:
231	nla_nest_cancel(msg, start_cap);
232
233	return -EMSGSIZE;
234}
235
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236int thermal_genl_event_tz_delete(struct param *p)
237	__attribute__((alias("thermal_genl_event_tz")));
238
239int thermal_genl_event_tz_enable(struct param *p)
240	__attribute__((alias("thermal_genl_event_tz")));
241
242int thermal_genl_event_tz_disable(struct param *p)
243	__attribute__((alias("thermal_genl_event_tz")));
244
245int thermal_genl_event_tz_trip_down(struct param *p)
246	__attribute__((alias("thermal_genl_event_tz_trip_up")));
247
248int thermal_genl_event_tz_trip_change(struct param *p)
249	__attribute__((alias("thermal_genl_event_tz_trip_add")));
 
 
 
250
251static cb_t event_cb[] = {
252	[THERMAL_GENL_EVENT_TZ_CREATE]		= thermal_genl_event_tz_create,
253	[THERMAL_GENL_EVENT_TZ_DELETE]		= thermal_genl_event_tz_delete,
254	[THERMAL_GENL_EVENT_TZ_ENABLE]		= thermal_genl_event_tz_enable,
255	[THERMAL_GENL_EVENT_TZ_DISABLE]		= thermal_genl_event_tz_disable,
256	[THERMAL_GENL_EVENT_TZ_TRIP_UP]		= thermal_genl_event_tz_trip_up,
257	[THERMAL_GENL_EVENT_TZ_TRIP_DOWN]	= thermal_genl_event_tz_trip_down,
258	[THERMAL_GENL_EVENT_TZ_TRIP_CHANGE]	= thermal_genl_event_tz_trip_change,
259	[THERMAL_GENL_EVENT_TZ_TRIP_ADD]	= thermal_genl_event_tz_trip_add,
260	[THERMAL_GENL_EVENT_TZ_TRIP_DELETE]	= thermal_genl_event_tz_trip_delete,
261	[THERMAL_GENL_EVENT_CDEV_ADD]		= thermal_genl_event_cdev_add,
262	[THERMAL_GENL_EVENT_CDEV_DELETE]	= thermal_genl_event_cdev_delete,
263	[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE]	= thermal_genl_event_cdev_state_update,
264	[THERMAL_GENL_EVENT_TZ_GOV_CHANGE]	= thermal_genl_event_gov_change,
265	[THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE] = thermal_genl_event_cpu_capability_change,
 
 
 
 
 
266};
267
268/*
269 * Generic netlink event encoding
270 */
271static int thermal_genl_send_event(enum thermal_genl_event event,
272				   struct param *p)
273{
274	struct sk_buff *msg;
275	int ret = -EMSGSIZE;
276	void *hdr;
277
 
 
 
278	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
279	if (!msg)
280		return -ENOMEM;
281	p->msg = msg;
282
283	hdr = genlmsg_put(msg, 0, 0, &thermal_gnl_family, 0, event);
284	if (!hdr)
285		goto out_free_msg;
286
287	ret = event_cb[event](p);
288	if (ret)
289		goto out_cancel_msg;
290
291	genlmsg_end(msg, hdr);
292
293	genlmsg_multicast(&thermal_gnl_family, msg, 0, 1, GFP_KERNEL);
294
295	return 0;
296
297out_cancel_msg:
298	genlmsg_cancel(msg, hdr);
299out_free_msg:
300	nlmsg_free(msg);
301
302	return ret;
303}
304
305int thermal_notify_tz_create(int tz_id, const char *name)
306{
307	struct param p = { .tz_id = tz_id, .name = name };
308
309	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_CREATE, &p);
310}
311
312int thermal_notify_tz_delete(int tz_id)
313{
314	struct param p = { .tz_id = tz_id };
315
316	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DELETE, &p);
317}
318
319int thermal_notify_tz_enable(int tz_id)
320{
321	struct param p = { .tz_id = tz_id };
322
323	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_ENABLE, &p);
324}
325
326int thermal_notify_tz_disable(int tz_id)
327{
328	struct param p = { .tz_id = tz_id };
329
330	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DISABLE, &p);
331}
332
333int thermal_notify_tz_trip_down(int tz_id, int trip_id, int temp)
 
334{
335	struct param p = { .tz_id = tz_id, .trip_id = trip_id, .temp = temp };
 
 
336
337	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DOWN, &p);
338}
339
340int thermal_notify_tz_trip_up(int tz_id, int trip_id, int temp)
 
341{
342	struct param p = { .tz_id = tz_id, .trip_id = trip_id, .temp = temp };
 
 
343
344	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_UP, &p);
345}
346
347int thermal_notify_tz_trip_add(int tz_id, int trip_id, int trip_type,
348			       int trip_temp, int trip_hyst)
349{
350	struct param p = { .tz_id = tz_id, .trip_id = trip_id,
351			   .trip_type = trip_type, .trip_temp = trip_temp,
352			   .trip_hyst = trip_hyst };
353
354	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_ADD, &p);
355}
356
357int thermal_notify_tz_trip_delete(int tz_id, int trip_id)
358{
359	struct param p = { .tz_id = tz_id, .trip_id = trip_id };
360
361	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DELETE, &p);
362}
363
364int thermal_notify_tz_trip_change(int tz_id, int trip_id, int trip_type,
365				  int trip_temp, int trip_hyst)
366{
367	struct param p = { .tz_id = tz_id, .trip_id = trip_id,
368			   .trip_type = trip_type, .trip_temp = trip_temp,
369			   .trip_hyst = trip_hyst };
370
371	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_CHANGE, &p);
372}
373
374int thermal_notify_cdev_state_update(int cdev_id, int cdev_state)
 
375{
376	struct param p = { .cdev_id = cdev_id, .cdev_state = cdev_state };
377
378	return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, &p);
379}
380
381int thermal_notify_cdev_add(int cdev_id, const char *name, int cdev_max_state)
382{
383	struct param p = { .cdev_id = cdev_id, .name = name,
384			   .cdev_max_state = cdev_max_state };
385
386	return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_ADD, &p);
387}
388
389int thermal_notify_cdev_delete(int cdev_id)
390{
391	struct param p = { .cdev_id = cdev_id };
392
393	return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_DELETE, &p);
394}
395
396int thermal_notify_tz_gov_change(int tz_id, const char *name)
 
397{
398	struct param p = { .tz_id = tz_id, .name = name };
399
400	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_GOV_CHANGE, &p);
401}
402
403int thermal_genl_cpu_capability_event(int count,
404				      struct thermal_genl_cpu_caps *caps)
405{
406	struct param p = { .cpu_capabilities_count = count, .cpu_capabilities = caps };
407
408	return thermal_genl_send_event(THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE, &p);
409}
410EXPORT_SYMBOL_GPL(thermal_genl_cpu_capability_event);
411
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
412/*************************** Command encoding ********************************/
413
414static int __thermal_genl_cmd_tz_get_id(struct thermal_zone_device *tz,
415					void *data)
416{
417	struct sk_buff *msg = data;
418
419	if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, tz->id) ||
420	    nla_put_string(msg, THERMAL_GENL_ATTR_TZ_NAME, tz->type))
421		return -EMSGSIZE;
422
423	return 0;
424}
425
426static int thermal_genl_cmd_tz_get_id(struct param *p)
427{
428	struct sk_buff *msg = p->msg;
429	struct nlattr *start_tz;
430	int ret;
431
432	start_tz = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ);
433	if (!start_tz)
434		return -EMSGSIZE;
435
436	ret = for_each_thermal_zone(__thermal_genl_cmd_tz_get_id, msg);
437	if (ret)
438		goto out_cancel_nest;
439
440	nla_nest_end(msg, start_tz);
441
442	return 0;
443
444out_cancel_nest:
445	nla_nest_cancel(msg, start_tz);
446
447	return ret;
448}
449
450static int thermal_genl_cmd_tz_get_trip(struct param *p)
451{
452	struct sk_buff *msg = p->msg;
453	struct thermal_zone_device *tz;
454	struct nlattr *start_trip;
455	int i, id;
456
457	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
458		return -EINVAL;
459
460	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
461
462	tz = thermal_zone_get_by_id(id);
463	if (!tz)
464		return -EINVAL;
465
466	start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ_TRIP);
467	if (!start_trip)
468		return -EMSGSIZE;
469
470	mutex_lock(&tz->lock);
471
472	for (i = 0; i < tz->num_trips; i++) {
 
473
474		enum thermal_trip_type type;
475		int temp, hyst = 0;
476
477		tz->ops->get_trip_type(tz, i, &type);
478		tz->ops->get_trip_temp(tz, i, &temp);
479		if (tz->ops->get_trip_hyst)
480			tz->ops->get_trip_hyst(tz, i, &hyst);
481
482		if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, i) ||
483		    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, type) ||
484		    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, temp) ||
485		    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, hyst))
486			goto out_cancel_nest;
487	}
488
489	mutex_unlock(&tz->lock);
490
491	nla_nest_end(msg, start_trip);
492
493	return 0;
494
495out_cancel_nest:
496	mutex_unlock(&tz->lock);
497
498	return -EMSGSIZE;
499}
500
501static int thermal_genl_cmd_tz_get_temp(struct param *p)
502{
503	struct sk_buff *msg = p->msg;
504	struct thermal_zone_device *tz;
505	int temp, ret, id;
506
507	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
508		return -EINVAL;
509
510	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
511
512	tz = thermal_zone_get_by_id(id);
513	if (!tz)
514		return -EINVAL;
515
516	ret = thermal_zone_get_temp(tz, &temp);
517	if (ret)
518		return ret;
519
520	if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
521	    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TEMP, temp))
522		return -EMSGSIZE;
523
524	return 0;
525}
526
527static int thermal_genl_cmd_tz_get_gov(struct param *p)
528{
529	struct sk_buff *msg = p->msg;
530	struct thermal_zone_device *tz;
531	int id, ret = 0;
532
533	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
534		return -EINVAL;
535
536	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
537
538	tz = thermal_zone_get_by_id(id);
539	if (!tz)
540		return -EINVAL;
541
542	mutex_lock(&tz->lock);
543
544	if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
545	    nla_put_string(msg, THERMAL_GENL_ATTR_TZ_GOV_NAME,
546			   tz->governor->name))
547		ret = -EMSGSIZE;
548
549	mutex_unlock(&tz->lock);
550
551	return ret;
552}
553
554static int __thermal_genl_cmd_cdev_get(struct thermal_cooling_device *cdev,
555				       void *data)
556{
557	struct sk_buff *msg = data;
558
559	if (nla_put_u32(msg, THERMAL_GENL_ATTR_CDEV_ID, cdev->id))
560		return -EMSGSIZE;
561
562	if (nla_put_string(msg, THERMAL_GENL_ATTR_CDEV_NAME, cdev->type))
563		return -EMSGSIZE;
564
565	return 0;
566}
567
568static int thermal_genl_cmd_cdev_get(struct param *p)
569{
570	struct sk_buff *msg = p->msg;
571	struct nlattr *start_cdev;
572	int ret;
573
574	start_cdev = nla_nest_start(msg, THERMAL_GENL_ATTR_CDEV);
575	if (!start_cdev)
576		return -EMSGSIZE;
577
578	ret = for_each_thermal_cooling_device(__thermal_genl_cmd_cdev_get, msg);
579	if (ret)
580		goto out_cancel_nest;
581
582	nla_nest_end(msg, start_cdev);
583
584	return 0;
585out_cancel_nest:
586	nla_nest_cancel(msg, start_cdev);
587
588	return ret;
589}
590
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
591static cb_t cmd_cb[] = {
592	[THERMAL_GENL_CMD_TZ_GET_ID]	= thermal_genl_cmd_tz_get_id,
593	[THERMAL_GENL_CMD_TZ_GET_TRIP]	= thermal_genl_cmd_tz_get_trip,
594	[THERMAL_GENL_CMD_TZ_GET_TEMP]	= thermal_genl_cmd_tz_get_temp,
595	[THERMAL_GENL_CMD_TZ_GET_GOV]	= thermal_genl_cmd_tz_get_gov,
596	[THERMAL_GENL_CMD_CDEV_GET]	= thermal_genl_cmd_cdev_get,
 
 
 
 
597};
598
599static int thermal_genl_cmd_dumpit(struct sk_buff *skb,
600				   struct netlink_callback *cb)
601{
602	struct param p = { .msg = skb };
603	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
604	int cmd = info->op.cmd;
605	int ret;
606	void *hdr;
607
608	hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0, cmd);
609	if (!hdr)
610		return -EMSGSIZE;
611
612	ret = cmd_cb[cmd](&p);
613	if (ret)
614		goto out_cancel_msg;
615
616	genlmsg_end(skb, hdr);
617
618	return 0;
619
620out_cancel_msg:
621	genlmsg_cancel(skb, hdr);
622
623	return ret;
624}
625
626static int thermal_genl_cmd_doit(struct sk_buff *skb,
627				 struct genl_info *info)
628{
629	struct param p = { .attrs = info->attrs };
630	struct sk_buff *msg;
631	void *hdr;
632	int cmd = info->genlhdr->cmd;
633	int ret = -EMSGSIZE;
634
635	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
636	if (!msg)
637		return -ENOMEM;
638	p.msg = msg;
639
640	hdr = genlmsg_put_reply(msg, info, &thermal_gnl_family, 0, cmd);
641	if (!hdr)
642		goto out_free_msg;
643
644	ret = cmd_cb[cmd](&p);
645	if (ret)
646		goto out_cancel_msg;
647
648	genlmsg_end(msg, hdr);
649
650	return genlmsg_reply(msg, info);
651
652out_cancel_msg:
653	genlmsg_cancel(msg, hdr);
654out_free_msg:
655	nlmsg_free(msg);
656
657	return ret;
658}
659
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
660static const struct genl_small_ops thermal_genl_ops[] = {
661	{
662		.cmd = THERMAL_GENL_CMD_TZ_GET_ID,
663		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
664		.dumpit = thermal_genl_cmd_dumpit,
665	},
666	{
667		.cmd = THERMAL_GENL_CMD_TZ_GET_TRIP,
668		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
669		.doit = thermal_genl_cmd_doit,
670	},
671	{
672		.cmd = THERMAL_GENL_CMD_TZ_GET_TEMP,
673		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
674		.doit = thermal_genl_cmd_doit,
675	},
676	{
677		.cmd = THERMAL_GENL_CMD_TZ_GET_GOV,
678		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
679		.doit = thermal_genl_cmd_doit,
680	},
681	{
682		.cmd = THERMAL_GENL_CMD_CDEV_GET,
683		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
684		.dumpit = thermal_genl_cmd_dumpit,
685	},
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
686};
687
688static struct genl_family thermal_gnl_family __ro_after_init = {
689	.hdrsize	= 0,
690	.name		= THERMAL_GENL_FAMILY_NAME,
691	.version	= THERMAL_GENL_VERSION,
692	.maxattr	= THERMAL_GENL_ATTR_MAX,
693	.policy		= thermal_genl_policy,
 
 
694	.small_ops	= thermal_genl_ops,
695	.n_small_ops	= ARRAY_SIZE(thermal_genl_ops),
696	.resv_start_op	= THERMAL_GENL_CMD_CDEV_GET + 1,
697	.mcgrps		= thermal_genl_mcgrps,
698	.n_mcgrps	= ARRAY_SIZE(thermal_genl_mcgrps),
699};
700
 
 
 
 
 
 
 
 
 
 
701int __init thermal_netlink_init(void)
702{
703	return genl_register_family(&thermal_gnl_family);
 
 
 
 
 
704}
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright 2020 Linaro Limited
  4 *
  5 * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
  6 *
  7 * Generic netlink for thermal management framework
  8 */
  9#include <linux/module.h>
 10#include <linux/notifier.h>
 11#include <linux/kernel.h>
 12#include <net/sock.h>
 13#include <net/genetlink.h>
 14#include <uapi/linux/thermal.h>
 15
 16#include "thermal_core.h"
 17
 18static const struct genl_multicast_group thermal_genl_mcgrps[] = {
 19	[THERMAL_GENL_SAMPLING_GROUP] = { .name = THERMAL_GENL_SAMPLING_GROUP_NAME, },
 20	[THERMAL_GENL_EVENT_GROUP]  = { .name = THERMAL_GENL_EVENT_GROUP_NAME,  },
 21};
 22
 23static const struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = {
 24	/* Thermal zone */
 25	[THERMAL_GENL_ATTR_TZ]			= { .type = NLA_NESTED },
 26	[THERMAL_GENL_ATTR_TZ_ID]		= { .type = NLA_U32 },
 27	[THERMAL_GENL_ATTR_TZ_TEMP]		= { .type = NLA_U32 },
 28	[THERMAL_GENL_ATTR_TZ_TRIP]		= { .type = NLA_NESTED },
 29	[THERMAL_GENL_ATTR_TZ_TRIP_ID]		= { .type = NLA_U32 },
 30	[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]	= { .type = NLA_U32 },
 31	[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]	= { .type = NLA_U32 },
 32	[THERMAL_GENL_ATTR_TZ_TRIP_HYST]	= { .type = NLA_U32 },
 33	[THERMAL_GENL_ATTR_TZ_MODE]		= { .type = NLA_U32 },
 34	[THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT]	= { .type = NLA_U32 },
 35	[THERMAL_GENL_ATTR_TZ_NAME]		= { .type = NLA_STRING,
 36						    .len = THERMAL_NAME_LENGTH },
 37	/* Governor(s) */
 38	[THERMAL_GENL_ATTR_TZ_GOV]		= { .type = NLA_NESTED },
 39	[THERMAL_GENL_ATTR_TZ_GOV_NAME]		= { .type = NLA_STRING,
 40						    .len = THERMAL_NAME_LENGTH },
 41	/* Cooling devices */
 42	[THERMAL_GENL_ATTR_CDEV]		= { .type = NLA_NESTED },
 43	[THERMAL_GENL_ATTR_CDEV_ID]		= { .type = NLA_U32 },
 44	[THERMAL_GENL_ATTR_CDEV_CUR_STATE]	= { .type = NLA_U32 },
 45	[THERMAL_GENL_ATTR_CDEV_MAX_STATE]	= { .type = NLA_U32 },
 46	[THERMAL_GENL_ATTR_CDEV_NAME]		= { .type = NLA_STRING,
 47						    .len = THERMAL_NAME_LENGTH },
 48	/* CPU capabilities */
 49	[THERMAL_GENL_ATTR_CPU_CAPABILITY]		= { .type = NLA_NESTED },
 50	[THERMAL_GENL_ATTR_CPU_CAPABILITY_ID]		= { .type = NLA_U32 },
 51	[THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE]	= { .type = NLA_U32 },
 52	[THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY]	= { .type = NLA_U32 },
 53
 54	/* Thresholds */
 55	[THERMAL_GENL_ATTR_THRESHOLD]		= { .type = NLA_NESTED },
 56	[THERMAL_GENL_ATTR_THRESHOLD_TEMP]	= { .type = NLA_U32 },
 57	[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]	= { .type = NLA_U32 },
 58};
 59
 60struct param {
 61	struct nlattr **attrs;
 62	struct sk_buff *msg;
 63	const char *name;
 64	int tz_id;
 65	int cdev_id;
 66	int trip_id;
 67	int trip_temp;
 68	int trip_type;
 69	int trip_hyst;
 70	int temp;
 71	int prev_temp;
 72	int direction;
 73	int cdev_state;
 74	int cdev_max_state;
 75	struct thermal_genl_cpu_caps *cpu_capabilities;
 76	int cpu_capabilities_count;
 77};
 78
 79typedef int (*cb_t)(struct param *);
 80
 81static struct genl_family thermal_genl_family;
 82static BLOCKING_NOTIFIER_HEAD(thermal_genl_chain);
 83
 84static int thermal_group_has_listeners(enum thermal_genl_multicast_groups group)
 85{
 86	return genl_has_listeners(&thermal_genl_family, &init_net, group);
 87}
 88
 89/************************** Sampling encoding *******************************/
 90
 91int thermal_genl_sampling_temp(int id, int temp)
 92{
 93	struct sk_buff *skb;
 94	void *hdr;
 95
 96	if (!thermal_group_has_listeners(THERMAL_GENL_SAMPLING_GROUP))
 97		return 0;
 98
 99	skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
100	if (!skb)
101		return -ENOMEM;
102
103	hdr = genlmsg_put(skb, 0, 0, &thermal_genl_family, 0,
104			  THERMAL_GENL_SAMPLING_TEMP);
105	if (!hdr)
106		goto out_free;
107
108	if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_ID, id))
109		goto out_cancel;
110
111	if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_TEMP, temp))
112		goto out_cancel;
113
114	genlmsg_end(skb, hdr);
115
116	genlmsg_multicast(&thermal_genl_family, skb, 0, THERMAL_GENL_SAMPLING_GROUP, GFP_KERNEL);
117
118	return 0;
119out_cancel:
120	genlmsg_cancel(skb, hdr);
121out_free:
122	nlmsg_free(skb);
123
124	return -EMSGSIZE;
125}
126
127/**************************** Event encoding *********************************/
128
129static int thermal_genl_event_tz_create(struct param *p)
130{
131	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
132	    nla_put_string(p->msg, THERMAL_GENL_ATTR_TZ_NAME, p->name))
133		return -EMSGSIZE;
134
135	return 0;
136}
137
138static int thermal_genl_event_tz(struct param *p)
139{
140	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id))
141		return -EMSGSIZE;
142
143	return 0;
144}
145
146static int thermal_genl_event_tz_trip_up(struct param *p)
147{
148	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
149	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id) ||
150	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TEMP, p->temp))
151		return -EMSGSIZE;
152
153	return 0;
154}
155
156static int thermal_genl_event_tz_trip_change(struct param *p)
157{
158	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
159	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id) ||
160	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, p->trip_type) ||
161	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, p->trip_temp) ||
162	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, p->trip_hyst))
163		return -EMSGSIZE;
164
165	return 0;
166}
167
 
 
 
 
 
 
 
 
 
168static int thermal_genl_event_cdev_add(struct param *p)
169{
170	if (nla_put_string(p->msg, THERMAL_GENL_ATTR_CDEV_NAME,
171			   p->name) ||
172	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID,
173			p->cdev_id) ||
174	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_MAX_STATE,
175			p->cdev_max_state))
176		return -EMSGSIZE;
177
178	return 0;
179}
180
181static int thermal_genl_event_cdev_delete(struct param *p)
182{
183	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID, p->cdev_id))
184		return -EMSGSIZE;
185
186	return 0;
187}
188
189static int thermal_genl_event_cdev_state_update(struct param *p)
190{
191	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID,
192			p->cdev_id) ||
193	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_CUR_STATE,
194			p->cdev_state))
195		return -EMSGSIZE;
196
197	return 0;
198}
199
200static int thermal_genl_event_gov_change(struct param *p)
201{
202	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
203	    nla_put_string(p->msg, THERMAL_GENL_ATTR_GOV_NAME, p->name))
204		return -EMSGSIZE;
205
206	return 0;
207}
208
209static int thermal_genl_event_cpu_capability_change(struct param *p)
210{
211	struct thermal_genl_cpu_caps *cpu_cap = p->cpu_capabilities;
212	struct sk_buff *msg = p->msg;
213	struct nlattr *start_cap;
214	int i;
215
216	start_cap = nla_nest_start(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY);
217	if (!start_cap)
218		return -EMSGSIZE;
219
220	for (i = 0; i < p->cpu_capabilities_count; ++i) {
221		if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_ID,
222				cpu_cap->cpu))
223			goto out_cancel_nest;
224
225		if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE,
226				cpu_cap->performance))
227			goto out_cancel_nest;
228
229		if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY,
230				cpu_cap->efficiency))
231			goto out_cancel_nest;
232
233		++cpu_cap;
234	}
235
236	nla_nest_end(msg, start_cap);
237
238	return 0;
239out_cancel_nest:
240	nla_nest_cancel(msg, start_cap);
241
242	return -EMSGSIZE;
243}
244
245static int thermal_genl_event_threshold_add(struct param *p)
246{
247	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
248	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, p->temp) ||
249	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_THRESHOLD_DIRECTION, p->direction))
250		return -EMSGSIZE;
251
252	return 0;
253}
254
255static int thermal_genl_event_threshold_flush(struct param *p)
256{
257	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id))
258		return -EMSGSIZE;
259
260	return 0;
261}
262
263static int thermal_genl_event_threshold_up(struct param *p)
264{
265	if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
266	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_PREV_TEMP, p->prev_temp) ||
267	    nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TEMP, p->temp))
268		return -EMSGSIZE;
269
270	return 0;
271}
272
273int thermal_genl_event_tz_delete(struct param *p)
274	__attribute__((alias("thermal_genl_event_tz")));
275
276int thermal_genl_event_tz_enable(struct param *p)
277	__attribute__((alias("thermal_genl_event_tz")));
278
279int thermal_genl_event_tz_disable(struct param *p)
280	__attribute__((alias("thermal_genl_event_tz")));
281
282int thermal_genl_event_tz_trip_down(struct param *p)
283	__attribute__((alias("thermal_genl_event_tz_trip_up")));
284
285int thermal_genl_event_threshold_delete(struct param *p)
286	__attribute__((alias("thermal_genl_event_threshold_add")));
287
288int thermal_genl_event_threshold_down(struct param *p)
289	__attribute__((alias("thermal_genl_event_threshold_up")));
290
291static cb_t event_cb[] = {
292	[THERMAL_GENL_EVENT_TZ_CREATE]		= thermal_genl_event_tz_create,
293	[THERMAL_GENL_EVENT_TZ_DELETE]		= thermal_genl_event_tz_delete,
294	[THERMAL_GENL_EVENT_TZ_ENABLE]		= thermal_genl_event_tz_enable,
295	[THERMAL_GENL_EVENT_TZ_DISABLE]		= thermal_genl_event_tz_disable,
296	[THERMAL_GENL_EVENT_TZ_TRIP_UP]		= thermal_genl_event_tz_trip_up,
297	[THERMAL_GENL_EVENT_TZ_TRIP_DOWN]	= thermal_genl_event_tz_trip_down,
298	[THERMAL_GENL_EVENT_TZ_TRIP_CHANGE]	= thermal_genl_event_tz_trip_change,
 
 
299	[THERMAL_GENL_EVENT_CDEV_ADD]		= thermal_genl_event_cdev_add,
300	[THERMAL_GENL_EVENT_CDEV_DELETE]	= thermal_genl_event_cdev_delete,
301	[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE]	= thermal_genl_event_cdev_state_update,
302	[THERMAL_GENL_EVENT_TZ_GOV_CHANGE]	= thermal_genl_event_gov_change,
303	[THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE] = thermal_genl_event_cpu_capability_change,
304	[THERMAL_GENL_EVENT_THRESHOLD_ADD]	= thermal_genl_event_threshold_add,
305	[THERMAL_GENL_EVENT_THRESHOLD_DELETE]	= thermal_genl_event_threshold_delete,
306	[THERMAL_GENL_EVENT_THRESHOLD_FLUSH]	= thermal_genl_event_threshold_flush,
307	[THERMAL_GENL_EVENT_THRESHOLD_DOWN]	= thermal_genl_event_threshold_down,
308	[THERMAL_GENL_EVENT_THRESHOLD_UP]	= thermal_genl_event_threshold_up,
309};
310
311/*
312 * Generic netlink event encoding
313 */
314static int thermal_genl_send_event(enum thermal_genl_event event,
315				   struct param *p)
316{
317	struct sk_buff *msg;
318	int ret = -EMSGSIZE;
319	void *hdr;
320
321	if (!thermal_group_has_listeners(THERMAL_GENL_EVENT_GROUP))
322		return 0;
323
324	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
325	if (!msg)
326		return -ENOMEM;
327	p->msg = msg;
328
329	hdr = genlmsg_put(msg, 0, 0, &thermal_genl_family, 0, event);
330	if (!hdr)
331		goto out_free_msg;
332
333	ret = event_cb[event](p);
334	if (ret)
335		goto out_cancel_msg;
336
337	genlmsg_end(msg, hdr);
338
339	genlmsg_multicast(&thermal_genl_family, msg, 0, THERMAL_GENL_EVENT_GROUP, GFP_KERNEL);
340
341	return 0;
342
343out_cancel_msg:
344	genlmsg_cancel(msg, hdr);
345out_free_msg:
346	nlmsg_free(msg);
347
348	return ret;
349}
350
351int thermal_notify_tz_create(const struct thermal_zone_device *tz)
352{
353	struct param p = { .tz_id = tz->id, .name = tz->type };
354
355	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_CREATE, &p);
356}
357
358int thermal_notify_tz_delete(const struct thermal_zone_device *tz)
359{
360	struct param p = { .tz_id = tz->id };
361
362	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DELETE, &p);
363}
364
365int thermal_notify_tz_enable(const struct thermal_zone_device *tz)
366{
367	struct param p = { .tz_id = tz->id };
368
369	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_ENABLE, &p);
370}
371
372int thermal_notify_tz_disable(const struct thermal_zone_device *tz)
373{
374	struct param p = { .tz_id = tz->id };
375
376	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DISABLE, &p);
377}
378
379int thermal_notify_tz_trip_down(const struct thermal_zone_device *tz,
380				const struct thermal_trip *trip)
381{
382	struct param p = { .tz_id = tz->id,
383			   .trip_id = thermal_zone_trip_id(tz, trip),
384			   .temp = tz->temperature };
385
386	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DOWN, &p);
387}
388
389int thermal_notify_tz_trip_up(const struct thermal_zone_device *tz,
390			      const struct thermal_trip *trip)
391{
392	struct param p = { .tz_id = tz->id,
393			   .trip_id = thermal_zone_trip_id(tz, trip),
394			   .temp = tz->temperature };
395
396	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_UP, &p);
397}
398
399int thermal_notify_tz_trip_change(const struct thermal_zone_device *tz,
400				  const struct thermal_trip *trip)
 
 
 
 
 
 
 
 
 
401{
402	struct param p = { .tz_id = tz->id,
403			   .trip_id = thermal_zone_trip_id(tz, trip),
404			   .trip_type = trip->type,
405			   .trip_temp = trip->temperature,
406			   .trip_hyst = trip->hysteresis };
 
 
 
 
 
 
407
408	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_CHANGE, &p);
409}
410
411int thermal_notify_cdev_state_update(const struct thermal_cooling_device *cdev,
412				     int state)
413{
414	struct param p = { .cdev_id = cdev->id, .cdev_state = state };
415
416	return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, &p);
417}
418
419int thermal_notify_cdev_add(const struct thermal_cooling_device *cdev)
420{
421	struct param p = { .cdev_id = cdev->id, .name = cdev->type,
422			   .cdev_max_state = cdev->max_state };
423
424	return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_ADD, &p);
425}
426
427int thermal_notify_cdev_delete(const struct thermal_cooling_device *cdev)
428{
429	struct param p = { .cdev_id = cdev->id };
430
431	return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_DELETE, &p);
432}
433
434int thermal_notify_tz_gov_change(const struct thermal_zone_device *tz,
435				 const char *name)
436{
437	struct param p = { .tz_id = tz->id, .name = name };
438
439	return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_GOV_CHANGE, &p);
440}
441
442int thermal_genl_cpu_capability_event(int count,
443				      struct thermal_genl_cpu_caps *caps)
444{
445	struct param p = { .cpu_capabilities_count = count, .cpu_capabilities = caps };
446
447	return thermal_genl_send_event(THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE, &p);
448}
449EXPORT_SYMBOL_GPL(thermal_genl_cpu_capability_event);
450
451int thermal_notify_threshold_add(const struct thermal_zone_device *tz,
452				 int temperature, int direction)
453{
454	struct param p = { .tz_id = tz->id, .temp = temperature, .direction = direction };
455
456	return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_ADD, &p);
457}
458
459int thermal_notify_threshold_delete(const struct thermal_zone_device *tz,
460				    int temperature, int direction)
461{
462	struct param p = { .tz_id = tz->id, .temp = temperature, .direction = direction };
463
464	return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_DELETE, &p);
465}
466
467int thermal_notify_threshold_flush(const struct thermal_zone_device *tz)
468{
469	struct param p = { .tz_id = tz->id };
470
471	return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_FLUSH, &p);
472}
473
474int thermal_notify_threshold_down(const struct thermal_zone_device *tz)
475{
476	struct param p = { .tz_id = tz->id, .temp = tz->temperature, .prev_temp = tz->last_temperature };
477
478	return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_DOWN, &p);
479}
480
481int thermal_notify_threshold_up(const struct thermal_zone_device *tz)
482{
483	struct param p = { .tz_id = tz->id, .temp = tz->temperature, .prev_temp = tz->last_temperature };
484
485	return thermal_genl_send_event(THERMAL_GENL_EVENT_THRESHOLD_UP, &p);
486}
487
488/*************************** Command encoding ********************************/
489
490static int __thermal_genl_cmd_tz_get_id(struct thermal_zone_device *tz,
491					void *data)
492{
493	struct sk_buff *msg = data;
494
495	if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, tz->id) ||
496	    nla_put_string(msg, THERMAL_GENL_ATTR_TZ_NAME, tz->type))
497		return -EMSGSIZE;
498
499	return 0;
500}
501
502static int thermal_genl_cmd_tz_get_id(struct param *p)
503{
504	struct sk_buff *msg = p->msg;
505	struct nlattr *start_tz;
506	int ret;
507
508	start_tz = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ);
509	if (!start_tz)
510		return -EMSGSIZE;
511
512	ret = for_each_thermal_zone(__thermal_genl_cmd_tz_get_id, msg);
513	if (ret)
514		goto out_cancel_nest;
515
516	nla_nest_end(msg, start_tz);
517
518	return 0;
519
520out_cancel_nest:
521	nla_nest_cancel(msg, start_tz);
522
523	return ret;
524}
525
526static int thermal_genl_cmd_tz_get_trip(struct param *p)
527{
528	struct sk_buff *msg = p->msg;
529	const struct thermal_trip_desc *td;
530	struct nlattr *start_trip;
531	int id;
532
533	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
534		return -EINVAL;
535
536	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
537
538	CLASS(thermal_zone_get_by_id, tz)(id);
539	if (!tz)
540		return -EINVAL;
541
542	start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ_TRIP);
543	if (!start_trip)
544		return -EMSGSIZE;
545
546	guard(thermal_zone)(tz);
547
548	for_each_trip_desc(tz, td) {
549		const struct thermal_trip *trip = &td->trip;
550
551		if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_ID,
552				thermal_zone_trip_id(tz, trip)) ||
553		    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, trip->type) ||
554		    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, trip->temperature) ||
555		    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, trip->hysteresis))
556			return -EMSGSIZE;
 
 
 
 
 
 
 
557	}
558
 
 
559	nla_nest_end(msg, start_trip);
560
561	return 0;
 
 
 
 
 
562}
563
564static int thermal_genl_cmd_tz_get_temp(struct param *p)
565{
566	struct sk_buff *msg = p->msg;
 
567	int temp, ret, id;
568
569	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
570		return -EINVAL;
571
572	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
573
574	CLASS(thermal_zone_get_by_id, tz)(id);
575	if (!tz)
576		return -EINVAL;
577
578	ret = thermal_zone_get_temp(tz, &temp);
579	if (ret)
580		return ret;
581
582	if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
583	    nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TEMP, temp))
584		return -EMSGSIZE;
585
586	return 0;
587}
588
589static int thermal_genl_cmd_tz_get_gov(struct param *p)
590{
591	struct sk_buff *msg = p->msg;
592	int id;
 
593
594	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
595		return -EINVAL;
596
597	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
598
599	CLASS(thermal_zone_get_by_id, tz)(id);
600	if (!tz)
601		return -EINVAL;
602
603	guard(thermal_zone)(tz);
604
605	if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
606	    nla_put_string(msg, THERMAL_GENL_ATTR_TZ_GOV_NAME,
607			   tz->governor->name))
608		return -EMSGSIZE;
 
 
609
610	return 0;
611}
612
613static int __thermal_genl_cmd_cdev_get(struct thermal_cooling_device *cdev,
614				       void *data)
615{
616	struct sk_buff *msg = data;
617
618	if (nla_put_u32(msg, THERMAL_GENL_ATTR_CDEV_ID, cdev->id))
619		return -EMSGSIZE;
620
621	if (nla_put_string(msg, THERMAL_GENL_ATTR_CDEV_NAME, cdev->type))
622		return -EMSGSIZE;
623
624	return 0;
625}
626
627static int thermal_genl_cmd_cdev_get(struct param *p)
628{
629	struct sk_buff *msg = p->msg;
630	struct nlattr *start_cdev;
631	int ret;
632
633	start_cdev = nla_nest_start(msg, THERMAL_GENL_ATTR_CDEV);
634	if (!start_cdev)
635		return -EMSGSIZE;
636
637	ret = for_each_thermal_cooling_device(__thermal_genl_cmd_cdev_get, msg);
638	if (ret)
639		goto out_cancel_nest;
640
641	nla_nest_end(msg, start_cdev);
642
643	return 0;
644out_cancel_nest:
645	nla_nest_cancel(msg, start_cdev);
646
647	return ret;
648}
649
650static int __thermal_genl_cmd_threshold_get(struct user_threshold *threshold, void *arg)
651{
652	struct sk_buff *msg = arg;
653
654	if (nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_TEMP, threshold->temperature) ||
655	    nla_put_u32(msg, THERMAL_GENL_ATTR_THRESHOLD_DIRECTION, threshold->direction))
656		return -1;
657
658	return 0;
659}
660
661static int thermal_genl_cmd_threshold_get(struct param *p)
662{
663	struct sk_buff *msg = p->msg;
664	struct nlattr *start_trip;
665	int id, ret;
666
667	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
668		return -EINVAL;
669
670	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
671
672	CLASS(thermal_zone_get_by_id, tz)(id);
673	if (!tz)
674		return -EINVAL;
675
676	start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_THRESHOLD);
677	if (!start_trip)
678		return -EMSGSIZE;
679
680	ret = thermal_thresholds_for_each(tz, __thermal_genl_cmd_threshold_get, msg);
681	if (ret)
682		return -EMSGSIZE;
683
684	nla_nest_end(msg, start_trip);
685
686	return 0;
687}
688
689static int thermal_genl_cmd_threshold_add(struct param *p)
690{
691	int id, temp, direction;
692
693	if (!capable(CAP_SYS_ADMIN))
694		return -EPERM;
695
696	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID] ||
697	    !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP] ||
698	    !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION])
699		return -EINVAL;
700
701	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
702	temp = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]);
703	direction = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]);
704
705	CLASS(thermal_zone_get_by_id, tz)(id);
706	if (!tz)
707		return -EINVAL;
708
709	guard(thermal_zone)(tz);
710
711	return thermal_thresholds_add(tz, temp, direction);
712}
713
714static int thermal_genl_cmd_threshold_delete(struct param *p)
715{
716	int id, temp, direction;
717
718	if (!capable(CAP_SYS_ADMIN))
719		return -EPERM;
720
721	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID] ||
722	    !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP] ||
723	    !p->attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION])
724		return -EINVAL;
725
726	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
727	temp = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_TEMP]);
728	direction = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_THRESHOLD_DIRECTION]);
729
730	CLASS(thermal_zone_get_by_id, tz)(id);
731	if (!tz)
732		return -EINVAL;
733
734	guard(thermal_zone)(tz);
735
736	return thermal_thresholds_delete(tz, temp, direction);
737}
738
739static int thermal_genl_cmd_threshold_flush(struct param *p)
740{
741	int id;
742
743	if (!capable(CAP_SYS_ADMIN))
744		return -EPERM;
745
746	if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
747		return -EINVAL;
748
749	id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
750
751	CLASS(thermal_zone_get_by_id, tz)(id);
752	if (!tz)
753		return -EINVAL;
754
755	guard(thermal_zone)(tz);
756
757	thermal_thresholds_flush(tz);
758
759	return 0;
760}
761
762static cb_t cmd_cb[] = {
763	[THERMAL_GENL_CMD_TZ_GET_ID]		= thermal_genl_cmd_tz_get_id,
764	[THERMAL_GENL_CMD_TZ_GET_TRIP]		= thermal_genl_cmd_tz_get_trip,
765	[THERMAL_GENL_CMD_TZ_GET_TEMP]		= thermal_genl_cmd_tz_get_temp,
766	[THERMAL_GENL_CMD_TZ_GET_GOV]		= thermal_genl_cmd_tz_get_gov,
767	[THERMAL_GENL_CMD_CDEV_GET]		= thermal_genl_cmd_cdev_get,
768	[THERMAL_GENL_CMD_THRESHOLD_GET]	= thermal_genl_cmd_threshold_get,
769	[THERMAL_GENL_CMD_THRESHOLD_ADD]	= thermal_genl_cmd_threshold_add,
770	[THERMAL_GENL_CMD_THRESHOLD_DELETE]	= thermal_genl_cmd_threshold_delete,
771	[THERMAL_GENL_CMD_THRESHOLD_FLUSH]	= thermal_genl_cmd_threshold_flush,
772};
773
774static int thermal_genl_cmd_dumpit(struct sk_buff *skb,
775				   struct netlink_callback *cb)
776{
777	struct param p = { .msg = skb };
778	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
779	int cmd = info->op.cmd;
780	int ret;
781	void *hdr;
782
783	hdr = genlmsg_put(skb, 0, 0, &thermal_genl_family, 0, cmd);
784	if (!hdr)
785		return -EMSGSIZE;
786
787	ret = cmd_cb[cmd](&p);
788	if (ret)
789		goto out_cancel_msg;
790
791	genlmsg_end(skb, hdr);
792
793	return 0;
794
795out_cancel_msg:
796	genlmsg_cancel(skb, hdr);
797
798	return ret;
799}
800
801static int thermal_genl_cmd_doit(struct sk_buff *skb,
802				 struct genl_info *info)
803{
804	struct param p = { .attrs = info->attrs };
805	struct sk_buff *msg;
806	void *hdr;
807	int cmd = info->genlhdr->cmd;
808	int ret = -EMSGSIZE;
809
810	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
811	if (!msg)
812		return -ENOMEM;
813	p.msg = msg;
814
815	hdr = genlmsg_put_reply(msg, info, &thermal_genl_family, 0, cmd);
816	if (!hdr)
817		goto out_free_msg;
818
819	ret = cmd_cb[cmd](&p);
820	if (ret)
821		goto out_cancel_msg;
822
823	genlmsg_end(msg, hdr);
824
825	return genlmsg_reply(msg, info);
826
827out_cancel_msg:
828	genlmsg_cancel(msg, hdr);
829out_free_msg:
830	nlmsg_free(msg);
831
832	return ret;
833}
834
835static int thermal_genl_bind(int mcgrp)
836{
837	struct thermal_genl_notify n = { .mcgrp = mcgrp };
838
839	if (WARN_ON_ONCE(mcgrp > THERMAL_GENL_MAX_GROUP))
840		return -EINVAL;
841
842	blocking_notifier_call_chain(&thermal_genl_chain, THERMAL_NOTIFY_BIND, &n);
843	return 0;
844}
845
846static void thermal_genl_unbind(int mcgrp)
847{
848	struct thermal_genl_notify n = { .mcgrp = mcgrp };
849
850	if (WARN_ON_ONCE(mcgrp > THERMAL_GENL_MAX_GROUP))
851		return;
852
853	blocking_notifier_call_chain(&thermal_genl_chain, THERMAL_NOTIFY_UNBIND, &n);
854}
855
856static const struct genl_small_ops thermal_genl_ops[] = {
857	{
858		.cmd = THERMAL_GENL_CMD_TZ_GET_ID,
859		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
860		.dumpit = thermal_genl_cmd_dumpit,
861	},
862	{
863		.cmd = THERMAL_GENL_CMD_TZ_GET_TRIP,
864		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
865		.doit = thermal_genl_cmd_doit,
866	},
867	{
868		.cmd = THERMAL_GENL_CMD_TZ_GET_TEMP,
869		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
870		.doit = thermal_genl_cmd_doit,
871	},
872	{
873		.cmd = THERMAL_GENL_CMD_TZ_GET_GOV,
874		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
875		.doit = thermal_genl_cmd_doit,
876	},
877	{
878		.cmd = THERMAL_GENL_CMD_CDEV_GET,
879		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
880		.dumpit = thermal_genl_cmd_dumpit,
881	},
882	{
883		.cmd = THERMAL_GENL_CMD_THRESHOLD_GET,
884		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
885		.doit = thermal_genl_cmd_doit,
886	},
887	{
888		.cmd = THERMAL_GENL_CMD_THRESHOLD_ADD,
889		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
890		.doit = thermal_genl_cmd_doit,
891	},
892	{
893		.cmd = THERMAL_GENL_CMD_THRESHOLD_DELETE,
894		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
895		.doit = thermal_genl_cmd_doit,
896	},
897	{
898		.cmd = THERMAL_GENL_CMD_THRESHOLD_FLUSH,
899		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
900		.doit = thermal_genl_cmd_doit,
901	},
902};
903
904static struct genl_family thermal_genl_family __ro_after_init = {
905	.hdrsize	= 0,
906	.name		= THERMAL_GENL_FAMILY_NAME,
907	.version	= THERMAL_GENL_VERSION,
908	.maxattr	= THERMAL_GENL_ATTR_MAX,
909	.policy		= thermal_genl_policy,
910	.bind		= thermal_genl_bind,
911	.unbind		= thermal_genl_unbind,
912	.small_ops	= thermal_genl_ops,
913	.n_small_ops	= ARRAY_SIZE(thermal_genl_ops),
914	.resv_start_op	= __THERMAL_GENL_CMD_MAX,
915	.mcgrps		= thermal_genl_mcgrps,
916	.n_mcgrps	= ARRAY_SIZE(thermal_genl_mcgrps),
917};
918
919int thermal_genl_register_notifier(struct notifier_block *nb)
920{
921	return blocking_notifier_chain_register(&thermal_genl_chain, nb);
922}
923
924int thermal_genl_unregister_notifier(struct notifier_block *nb)
925{
926	return blocking_notifier_chain_unregister(&thermal_genl_chain, nb);
927}
928
929int __init thermal_netlink_init(void)
930{
931	return genl_register_family(&thermal_genl_family);
932}
933
934void __init thermal_netlink_exit(void)
935{
936	genl_unregister_family(&thermal_genl_family);
937}