minstrel-rcd: add work-in-progress minstrel remote control daemon
[openwrt/staging/nbd.git] / package / utils / minstrel-rcd / src / phy.c
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (C) 2021 Felix Fietkau <nbd@nbd.name> */
3 #include <libubox/avl-cmp.h>
4 #include <glob.h>
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <stdio.h>
8 #include <libgen.h>
9 #include "rcd.h"
10
11 static void phy_update(struct vlist_tree *tree, struct vlist_node *node_new,
12 struct vlist_node *node_old);
13
14 VLIST_TREE(phy_list, avl_strcmp, phy_update, true, false);
15
16 static const char *
17 phy_file_path(struct phy *phy, const char *file)
18 {
19 static char path[64];
20
21 snprintf(path, sizeof(path), "/sys/kernel/debug/ieee80211/%s/rc/%s", phy_name(phy), file);
22
23 return path;
24 }
25
26 static int
27 phy_event_read_buf(struct phy *phy, char *buf)
28 {
29 char *cur, *next;
30 int len;
31
32 for (cur = buf; (next = strchr(cur, '\n')); cur = next + 1) {
33 *next = 0;
34
35 rcd_client_phy_event(phy, cur);
36 }
37
38 len = strlen(cur);
39 if (cur > buf)
40 memmove(buf, cur, len + 1);
41
42 return len;
43 }
44
45 static void
46 phy_event_cb(struct uloop_fd *fd, unsigned int events)
47 {
48 struct phy *phy = container_of(fd, struct phy, event_fd);
49 char buf[512];
50 int len, offset = 0;
51
52 while (1) {
53 len = read(fd->fd, buf + offset, sizeof(buf) - 1 - offset);
54 if (len < 0) {
55 if (errno == EAGAIN)
56 return;
57
58 if (errno == EINTR)
59 continue;
60
61 vlist_delete(&phy_list, &phy->node);
62 return;
63 }
64
65 if (!len)
66 return;
67
68 buf[offset + len] = 0;
69 offset = phy_event_read_buf(phy, buf);
70 }
71 }
72
73 static void
74 phy_init(struct phy *phy)
75 {
76 phy->control_fd = -1;
77 }
78
79 static void
80 phy_add(struct phy *phy)
81 {
82 int cfd, efd;
83
84 cfd = open(phy_file_path(phy, "api_control"), O_WRONLY);
85 if (cfd < 0)
86 goto remove;
87
88 efd = open(phy_file_path(phy, "api_event"), O_RDONLY);
89 if (efd < 0)
90 goto close_cfd;
91
92 phy->control_fd = cfd;
93 phy->event_fd.fd = efd;
94 phy->event_fd.cb = phy_event_cb;
95 uloop_fd_add(&phy->event_fd, ULOOP_READ);
96
97 rcd_client_set_phy_state(NULL, phy, true);
98 return;
99
100 close_cfd:
101 close(cfd);
102 remove:
103 vlist_delete(&phy_list, &phy->node);
104 }
105
106 static void
107 phy_remove(struct phy *phy)
108 {
109 if (phy->control_fd < 0)
110 goto out;
111
112 rcd_client_set_phy_state(NULL, phy, false);
113 uloop_fd_delete(&phy->event_fd);
114 close(phy->control_fd);
115 close(phy->event_fd.fd);
116
117 out:
118 free(phy);
119 }
120
121 static void
122 phy_update(struct vlist_tree *tree, struct vlist_node *node_new,
123 struct vlist_node *node_old)
124 {
125 struct phy *phy_new = node_new ? container_of(node_new, struct phy, node) : NULL;
126 struct phy *phy_old = node_old ? container_of(node_old, struct phy, node) : NULL;
127
128 if (phy_new && phy_old)
129 phy_remove(phy_new);
130 else if (phy_new)
131 phy_add(phy_new);
132 else
133 phy_remove(phy_old);
134 }
135
136 static void phy_refresh_timer(struct uloop_timeout *t)
137 {
138 unsigned int i;
139 glob_t gl;
140
141 glob("/sys/kernel/debug/ieee80211/phy*", 0, NULL, &gl);
142 for (i = 0; i < gl.gl_pathc; i++) {
143 struct phy *phy;
144 char *name, *name_buf;
145
146 name = basename(gl.gl_pathv[i]);
147 phy = calloc_a(sizeof(*phy), &name_buf, strlen(name) + 1);
148 phy_init(phy);
149 vlist_add(&phy_list, &phy->node, strcpy(name_buf, name));
150 }
151 globfree(&gl);
152
153 uloop_timeout_set(t, 1000);
154 }
155
156 void rcd_phy_init_client(struct client *cl)
157 {
158 struct phy *phy;
159
160 vlist_for_each_element(&phy_list, phy, node)
161 rcd_client_set_phy_state(cl, phy, true);
162 }
163
164 void rcd_phy_dump(struct client *cl, struct phy *phy)
165 {
166 char buf[128];
167 FILE *f;
168
169 f = fopen(phy_file_path(phy, "api_info"), "r");
170 if (!f)
171 return;
172
173 while (fgets(buf, sizeof(buf), f) != NULL)
174 client_printf(cl, "*;0;%s", buf);
175
176 fclose(f);
177 }
178
179 void rcd_phy_control(struct client *cl, char *data)
180 {
181 struct phy *phy;
182 const char *err;
183 char *sep;
184
185 sep = strchr(data, ';');
186 if (!sep) {
187 err = "Syntax error";
188 goto error;
189 }
190
191 *sep = 0;
192 phy = vlist_find(&phy_list, data, phy, node);
193 if (!phy) {
194 err = "PHY not found";
195 goto error;
196 }
197
198 data = sep + 1;
199 retry:
200 if (write(phy->control_fd, data, strlen(data)) < 0) {
201 if (errno == EINTR || errno == EAGAIN)
202 goto retry;
203
204 err = strerror(errno);
205 goto error;
206 }
207
208 return;
209
210 error:
211 client_printf(cl, "*;0;#error;%s\n", err);
212 }
213
214 void rcd_phy_init(void)
215 {
216 static struct uloop_timeout t = {
217 .cb = phy_refresh_timer
218 };
219
220 uloop_timeout_set(&t, 1);
221 }