xref: /openbmc/linux/tools/lib/thermal/thermal_nl.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1*47c4b0deSDaniel Lezcano // SPDX-License-Identifier: LGPL-2.1+
2*47c4b0deSDaniel Lezcano // Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org>
3*47c4b0deSDaniel Lezcano #include <errno.h>
4*47c4b0deSDaniel Lezcano #include <stdio.h>
5*47c4b0deSDaniel Lezcano #include <stdlib.h>
6*47c4b0deSDaniel Lezcano #include <unistd.h>
7*47c4b0deSDaniel Lezcano 
8*47c4b0deSDaniel Lezcano #include <thermal.h>
9*47c4b0deSDaniel Lezcano #include "thermal_nl.h"
10*47c4b0deSDaniel Lezcano 
11*47c4b0deSDaniel Lezcano struct handler_args {
12*47c4b0deSDaniel Lezcano 	const char *group;
13*47c4b0deSDaniel Lezcano 	int id;
14*47c4b0deSDaniel Lezcano };
15*47c4b0deSDaniel Lezcano 
16*47c4b0deSDaniel Lezcano static __thread int err;
17*47c4b0deSDaniel Lezcano static __thread int done;
18*47c4b0deSDaniel Lezcano 
nl_seq_check_handler(struct nl_msg * msg,void * arg)19*47c4b0deSDaniel Lezcano static int nl_seq_check_handler(struct nl_msg *msg, void *arg)
20*47c4b0deSDaniel Lezcano {
21*47c4b0deSDaniel Lezcano 	return NL_OK;
22*47c4b0deSDaniel Lezcano }
23*47c4b0deSDaniel Lezcano 
nl_error_handler(struct sockaddr_nl * nla,struct nlmsgerr * nl_err,void * arg)24*47c4b0deSDaniel Lezcano static int nl_error_handler(struct sockaddr_nl *nla, struct nlmsgerr *nl_err,
25*47c4b0deSDaniel Lezcano 			    void *arg)
26*47c4b0deSDaniel Lezcano {
27*47c4b0deSDaniel Lezcano 	int *ret = arg;
28*47c4b0deSDaniel Lezcano 
29*47c4b0deSDaniel Lezcano 	if (ret)
30*47c4b0deSDaniel Lezcano 		*ret = nl_err->error;
31*47c4b0deSDaniel Lezcano 
32*47c4b0deSDaniel Lezcano 	return NL_STOP;
33*47c4b0deSDaniel Lezcano }
34*47c4b0deSDaniel Lezcano 
nl_finish_handler(struct nl_msg * msg,void * arg)35*47c4b0deSDaniel Lezcano static int nl_finish_handler(struct nl_msg *msg, void *arg)
36*47c4b0deSDaniel Lezcano {
37*47c4b0deSDaniel Lezcano 	int *ret = arg;
38*47c4b0deSDaniel Lezcano 
39*47c4b0deSDaniel Lezcano 	if (ret)
40*47c4b0deSDaniel Lezcano 		*ret = 1;
41*47c4b0deSDaniel Lezcano 
42*47c4b0deSDaniel Lezcano 	return NL_OK;
43*47c4b0deSDaniel Lezcano }
44*47c4b0deSDaniel Lezcano 
nl_ack_handler(struct nl_msg * msg,void * arg)45*47c4b0deSDaniel Lezcano static int nl_ack_handler(struct nl_msg *msg, void *arg)
46*47c4b0deSDaniel Lezcano {
47*47c4b0deSDaniel Lezcano 	int *ret = arg;
48*47c4b0deSDaniel Lezcano 
49*47c4b0deSDaniel Lezcano 	if (ret)
50*47c4b0deSDaniel Lezcano 		*ret = 1;
51*47c4b0deSDaniel Lezcano 
52*47c4b0deSDaniel Lezcano 	return NL_OK;
53*47c4b0deSDaniel Lezcano }
54*47c4b0deSDaniel Lezcano 
nl_send_msg(struct nl_sock * sock,struct nl_cb * cb,struct nl_msg * msg,int (* rx_handler)(struct nl_msg *,void *),void * data)55*47c4b0deSDaniel Lezcano int nl_send_msg(struct nl_sock *sock, struct nl_cb *cb, struct nl_msg *msg,
56*47c4b0deSDaniel Lezcano 		int (*rx_handler)(struct nl_msg *, void *), void *data)
57*47c4b0deSDaniel Lezcano {
58*47c4b0deSDaniel Lezcano 	if (!rx_handler)
59*47c4b0deSDaniel Lezcano 		return THERMAL_ERROR;
60*47c4b0deSDaniel Lezcano 
61*47c4b0deSDaniel Lezcano 	err = nl_send_auto_complete(sock, msg);
62*47c4b0deSDaniel Lezcano 	if (err < 0)
63*47c4b0deSDaniel Lezcano 		return err;
64*47c4b0deSDaniel Lezcano 
65*47c4b0deSDaniel Lezcano 	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, rx_handler, data);
66*47c4b0deSDaniel Lezcano 
67*47c4b0deSDaniel Lezcano 	err = done = 0;
68*47c4b0deSDaniel Lezcano 
69*47c4b0deSDaniel Lezcano 	while (err == 0 && done == 0)
70*47c4b0deSDaniel Lezcano 		nl_recvmsgs(sock, cb);
71*47c4b0deSDaniel Lezcano 
72*47c4b0deSDaniel Lezcano 	return err;
73*47c4b0deSDaniel Lezcano }
74*47c4b0deSDaniel Lezcano 
nl_family_handler(struct nl_msg * msg,void * arg)75*47c4b0deSDaniel Lezcano static int nl_family_handler(struct nl_msg *msg, void *arg)
76*47c4b0deSDaniel Lezcano {
77*47c4b0deSDaniel Lezcano 	struct handler_args *grp = arg;
78*47c4b0deSDaniel Lezcano 	struct nlattr *tb[CTRL_ATTR_MAX + 1];
79*47c4b0deSDaniel Lezcano 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
80*47c4b0deSDaniel Lezcano 	struct nlattr *mcgrp;
81*47c4b0deSDaniel Lezcano 	int rem_mcgrp;
82*47c4b0deSDaniel Lezcano 
83*47c4b0deSDaniel Lezcano 	nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
84*47c4b0deSDaniel Lezcano 		  genlmsg_attrlen(gnlh, 0), NULL);
85*47c4b0deSDaniel Lezcano 
86*47c4b0deSDaniel Lezcano 	if (!tb[CTRL_ATTR_MCAST_GROUPS])
87*47c4b0deSDaniel Lezcano 		return THERMAL_ERROR;
88*47c4b0deSDaniel Lezcano 
89*47c4b0deSDaniel Lezcano 	nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp) {
90*47c4b0deSDaniel Lezcano 
91*47c4b0deSDaniel Lezcano 		struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1];
92*47c4b0deSDaniel Lezcano 
93*47c4b0deSDaniel Lezcano 		nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX,
94*47c4b0deSDaniel Lezcano 			  nla_data(mcgrp), nla_len(mcgrp), NULL);
95*47c4b0deSDaniel Lezcano 
96*47c4b0deSDaniel Lezcano 		if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] ||
97*47c4b0deSDaniel Lezcano 		    !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID])
98*47c4b0deSDaniel Lezcano 			continue;
99*47c4b0deSDaniel Lezcano 
100*47c4b0deSDaniel Lezcano 		if (strncmp(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]),
101*47c4b0deSDaniel Lezcano 			    grp->group,
102*47c4b0deSDaniel Lezcano 			    nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME])))
103*47c4b0deSDaniel Lezcano 			continue;
104*47c4b0deSDaniel Lezcano 
105*47c4b0deSDaniel Lezcano 		grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]);
106*47c4b0deSDaniel Lezcano 
107*47c4b0deSDaniel Lezcano 		break;
108*47c4b0deSDaniel Lezcano 	}
109*47c4b0deSDaniel Lezcano 
110*47c4b0deSDaniel Lezcano 	return THERMAL_SUCCESS;
111*47c4b0deSDaniel Lezcano }
112*47c4b0deSDaniel Lezcano 
nl_get_multicast_id(struct nl_sock * sock,struct nl_cb * cb,const char * family,const char * group)113*47c4b0deSDaniel Lezcano static int nl_get_multicast_id(struct nl_sock *sock, struct nl_cb *cb,
114*47c4b0deSDaniel Lezcano 			       const char *family, const char *group)
115*47c4b0deSDaniel Lezcano {
116*47c4b0deSDaniel Lezcano 	struct nl_msg *msg;
117*47c4b0deSDaniel Lezcano 	int ret = 0, ctrlid;
118*47c4b0deSDaniel Lezcano 	struct handler_args grp = {
119*47c4b0deSDaniel Lezcano 		.group = group,
120*47c4b0deSDaniel Lezcano 		.id = -ENOENT,
121*47c4b0deSDaniel Lezcano 	};
122*47c4b0deSDaniel Lezcano 
123*47c4b0deSDaniel Lezcano 	msg = nlmsg_alloc();
124*47c4b0deSDaniel Lezcano 	if (!msg)
125*47c4b0deSDaniel Lezcano 		return THERMAL_ERROR;
126*47c4b0deSDaniel Lezcano 
127*47c4b0deSDaniel Lezcano 	ctrlid = genl_ctrl_resolve(sock, "nlctrl");
128*47c4b0deSDaniel Lezcano 
129*47c4b0deSDaniel Lezcano 	genlmsg_put(msg, 0, 0, ctrlid, 0, 0, CTRL_CMD_GETFAMILY, 0);
130*47c4b0deSDaniel Lezcano 
131*47c4b0deSDaniel Lezcano 	nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, family);
132*47c4b0deSDaniel Lezcano 
133*47c4b0deSDaniel Lezcano 	ret = nl_send_msg(sock, cb, msg, nl_family_handler, &grp);
134*47c4b0deSDaniel Lezcano 	if (ret)
135*47c4b0deSDaniel Lezcano 		goto nla_put_failure;
136*47c4b0deSDaniel Lezcano 
137*47c4b0deSDaniel Lezcano 	ret = grp.id;
138*47c4b0deSDaniel Lezcano 
139*47c4b0deSDaniel Lezcano nla_put_failure:
140*47c4b0deSDaniel Lezcano 	nlmsg_free(msg);
141*47c4b0deSDaniel Lezcano 	return ret;
142*47c4b0deSDaniel Lezcano }
143*47c4b0deSDaniel Lezcano 
nl_thermal_connect(struct nl_sock ** nl_sock,struct nl_cb ** nl_cb)144*47c4b0deSDaniel Lezcano int nl_thermal_connect(struct nl_sock **nl_sock, struct nl_cb **nl_cb)
145*47c4b0deSDaniel Lezcano {
146*47c4b0deSDaniel Lezcano 	struct nl_cb *cb;
147*47c4b0deSDaniel Lezcano 	struct nl_sock *sock;
148*47c4b0deSDaniel Lezcano 
149*47c4b0deSDaniel Lezcano 	cb = nl_cb_alloc(NL_CB_DEFAULT);
150*47c4b0deSDaniel Lezcano 	if (!cb)
151*47c4b0deSDaniel Lezcano 		return THERMAL_ERROR;
152*47c4b0deSDaniel Lezcano 
153*47c4b0deSDaniel Lezcano 	sock = nl_socket_alloc();
154*47c4b0deSDaniel Lezcano 	if (!sock)
155*47c4b0deSDaniel Lezcano 		goto out_cb_free;
156*47c4b0deSDaniel Lezcano 
157*47c4b0deSDaniel Lezcano 	if (genl_connect(sock))
158*47c4b0deSDaniel Lezcano 		goto out_socket_free;
159*47c4b0deSDaniel Lezcano 
160*47c4b0deSDaniel Lezcano 	if (nl_cb_err(cb, NL_CB_CUSTOM, nl_error_handler, &err) ||
161*47c4b0deSDaniel Lezcano 	    nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, nl_finish_handler, &done) ||
162*47c4b0deSDaniel Lezcano 	    nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, nl_ack_handler, &done) ||
163*47c4b0deSDaniel Lezcano 	    nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nl_seq_check_handler, &done))
164*47c4b0deSDaniel Lezcano 		return THERMAL_ERROR;
165*47c4b0deSDaniel Lezcano 
166*47c4b0deSDaniel Lezcano 	*nl_sock = sock;
167*47c4b0deSDaniel Lezcano 	*nl_cb = cb;
168*47c4b0deSDaniel Lezcano 
169*47c4b0deSDaniel Lezcano 	return THERMAL_SUCCESS;
170*47c4b0deSDaniel Lezcano 
171*47c4b0deSDaniel Lezcano out_socket_free:
172*47c4b0deSDaniel Lezcano 	nl_socket_free(sock);
173*47c4b0deSDaniel Lezcano out_cb_free:
174*47c4b0deSDaniel Lezcano 	nl_cb_put(cb);
175*47c4b0deSDaniel Lezcano 	return THERMAL_ERROR;
176*47c4b0deSDaniel Lezcano }
177*47c4b0deSDaniel Lezcano 
nl_thermal_disconnect(struct nl_sock * nl_sock,struct nl_cb * nl_cb)178*47c4b0deSDaniel Lezcano void nl_thermal_disconnect(struct nl_sock *nl_sock, struct nl_cb *nl_cb)
179*47c4b0deSDaniel Lezcano {
180*47c4b0deSDaniel Lezcano 	nl_close(nl_sock);
181*47c4b0deSDaniel Lezcano 	nl_socket_free(nl_sock);
182*47c4b0deSDaniel Lezcano 	nl_cb_put(nl_cb);
183*47c4b0deSDaniel Lezcano }
184*47c4b0deSDaniel Lezcano 
nl_unsubscribe_thermal(struct nl_sock * nl_sock,struct nl_cb * nl_cb,const char * group)185*47c4b0deSDaniel Lezcano int nl_unsubscribe_thermal(struct nl_sock *nl_sock, struct nl_cb *nl_cb,
186*47c4b0deSDaniel Lezcano 			   const char *group)
187*47c4b0deSDaniel Lezcano {
188*47c4b0deSDaniel Lezcano 	int mcid;
189*47c4b0deSDaniel Lezcano 
190*47c4b0deSDaniel Lezcano 	mcid = nl_get_multicast_id(nl_sock, nl_cb, THERMAL_GENL_FAMILY_NAME,
191*47c4b0deSDaniel Lezcano 				   group);
192*47c4b0deSDaniel Lezcano 	if (mcid < 0)
193*47c4b0deSDaniel Lezcano 		return THERMAL_ERROR;
194*47c4b0deSDaniel Lezcano 
195*47c4b0deSDaniel Lezcano 	if (nl_socket_drop_membership(nl_sock, mcid))
196*47c4b0deSDaniel Lezcano 		return THERMAL_ERROR;
197*47c4b0deSDaniel Lezcano 
198*47c4b0deSDaniel Lezcano 	return THERMAL_SUCCESS;
199*47c4b0deSDaniel Lezcano }
200*47c4b0deSDaniel Lezcano 
nl_subscribe_thermal(struct nl_sock * nl_sock,struct nl_cb * nl_cb,const char * group)201*47c4b0deSDaniel Lezcano int nl_subscribe_thermal(struct nl_sock *nl_sock, struct nl_cb *nl_cb,
202*47c4b0deSDaniel Lezcano 			 const char *group)
203*47c4b0deSDaniel Lezcano {
204*47c4b0deSDaniel Lezcano 	int mcid;
205*47c4b0deSDaniel Lezcano 
206*47c4b0deSDaniel Lezcano 	mcid = nl_get_multicast_id(nl_sock, nl_cb, THERMAL_GENL_FAMILY_NAME,
207*47c4b0deSDaniel Lezcano 				   group);
208*47c4b0deSDaniel Lezcano 	if (mcid < 0)
209*47c4b0deSDaniel Lezcano 		return THERMAL_ERROR;
210*47c4b0deSDaniel Lezcano 
211*47c4b0deSDaniel Lezcano 	if (nl_socket_add_membership(nl_sock, mcid))
212*47c4b0deSDaniel Lezcano 		return THERMAL_ERROR;
213*47c4b0deSDaniel Lezcano 
214*47c4b0deSDaniel Lezcano 	return THERMAL_SUCCESS;
215*47c4b0deSDaniel Lezcano }
216