rssileds: improve performance
[openwrt/openwrt.git] / package / network / utils / rssileds / src / rssileds.c
1 /*
2 * configurable RSSI LED control daemon for OpenWrt
3 * (c) 2012 Allnet GmbH, Daniel Golle <dgolle@allnet.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * The author may be reached as dgolle@allnet.de, or
20 * ALLNET GmbH
21 * Maistr. 2
22 * D-82110 Germering
23 * Germany
24 *
25 */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <signal.h>
32 #include <unistd.h>
33 #include <syslog.h>
34
35 #include "iwinfo.h"
36
37 #define RUN_DIR "/var/run"
38 #define LEDS_BASEPATH "/sys/class/leds/"
39 #define BACKEND_RETRY_DELAY 500000
40
41 char *ifname;
42 int qual_max;
43
44 struct led {
45 char *sysfspath;
46 FILE *controlfd;
47 unsigned char state;
48 };
49
50 typedef struct rule rule_t;
51 struct rule {
52 struct led *led;
53 int minq;
54 int maxq;
55 int boffset;
56 int bfactor;
57 rule_t *next;
58 };
59
60 void log_rules(rule_t *rules)
61 {
62 rule_t *rule = rules;
63 while (rule)
64 {
65 syslog(LOG_INFO, " %s r: %d..%d, o: %d, f: %d\n",
66 rule->led->sysfspath,
67 rule->minq, rule->maxq,
68 rule->boffset, rule->bfactor);
69 rule = rule->next;
70 }
71 }
72
73 int set_led(struct led *led, unsigned char value)
74 {
75 char buf[8];
76
77 if ( ! led )
78 return -1;
79
80 if ( ! led->controlfd )
81 return -1;
82
83 if ( led->state == value )
84 return 0;
85
86 snprintf(buf, 8, "%d", value);
87
88 rewind(led->controlfd);
89
90 if ( ! fwrite(buf, sizeof(char), strlen(buf), led->controlfd) )
91 return -2;
92
93 fflush(led->controlfd);
94 led->state=value;
95
96 return 0;
97 }
98
99 int init_led(struct led **led, char *ledname)
100 {
101 struct led *newled;
102 struct stat statbuffer;
103 int status;
104 char *bp;
105 FILE *bfp;
106
107 bp = calloc(sizeof(char), strlen(ledname) + strlen(LEDS_BASEPATH) + 12);
108 if ( ! bp )
109 goto return_error;
110
111 sprintf(bp, "%s%s/brightness", LEDS_BASEPATH, ledname);
112
113 status = stat(bp, &statbuffer);
114 if ( status )
115 goto cleanup_fname;
116
117 bfp = fopen( bp, "w" );
118 if ( !bfp )
119 goto cleanup_fname;
120
121 if ( ferror(bfp) )
122 goto cleanup_fp;
123
124 /* sysfs path exists and, allocate LED struct */
125 newled = calloc(sizeof(struct led),1);
126 if ( !newled )
127 goto cleanup_fp;
128
129 newled->sysfspath = bp;
130 newled->controlfd = bfp;
131
132 *led = newled;
133
134 if ( set_led(newled, 255) )
135 goto cleanup_fp;
136
137 if ( set_led(newled, 0) )
138 goto cleanup_fp;
139
140 return 0;
141
142 cleanup_fp:
143 fclose(bfp);
144 cleanup_fname:
145 free(bp);
146 return_error:
147 syslog(LOG_CRIT, "can't open LED %s\n", ledname);
148 *led = NULL;
149 return -1;
150 }
151
152 void close_led(struct led **led)
153 {
154 fclose((*led)->controlfd);
155 free((*led)->sysfspath);
156 free((*led));
157 (*led)=NULL;
158 }
159
160
161 int quality(const struct iwinfo_ops *iw, const char *ifname)
162 {
163 int qual;
164
165 if ( ! iw ) return -1;
166
167 if (qual_max < 1)
168 if (iw->quality_max(ifname, &qual_max))
169 return -1;
170
171 if (iw->quality(ifname, &qual))
172 return -1;
173
174 return ( qual * 100 ) / qual_max ;
175 }
176
177 int open_backend(const struct iwinfo_ops **iw, const char *ifname)
178 {
179 *iw = iwinfo_backend(ifname);
180
181 if (!(*iw))
182 return 1;
183
184 return 0;
185 }
186
187 void update_leds(rule_t *rules, int q)
188 {
189 rule_t *rule = rules;
190 while (rule)
191 {
192 int b;
193 /* offset and factore correction according to rule */
194 b = ( q + rule->boffset ) * rule->bfactor;
195 if ( b < 0 )
196 b=0;
197 if ( b > 255 )
198 b=255;
199
200 if ( q >= rule->minq && q <= rule->maxq )
201 set_led(rule->led, (unsigned char)b);
202 else
203 set_led(rule->led, 0);
204
205 rule = rule->next;
206 }
207 }
208
209 int main(int argc, char **argv)
210 {
211 int i,q,q0,r,s;
212 const struct iwinfo_ops *iw = NULL;
213 rule_t *headrule = NULL, *currentrule = NULL;
214
215 if (argc < 9 || ( (argc-4) % 5 != 0 ) )
216 {
217 printf("syntax: %s (ifname) (refresh) (threshold) (rule) [rule] ...\n", argv[0]);
218 printf(" rule: (sysfs-name) (minq) (maxq) (offset) (factore)\n");
219 return 1;
220 }
221
222 ifname = argv[1];
223
224 /* refresh interval */
225 if ( sscanf(argv[2], "%d", &r) != 1 )
226 return 1;
227
228 /* sustain threshold */
229 if ( sscanf(argv[3], "%d", &s) != 1 )
230 return 1;
231
232 openlog("rssileds", LOG_PID, LOG_DAEMON);
233 syslog(LOG_INFO, "monitoring %s, refresh rate %d, threshold %d\n", ifname, r, s);
234
235 currentrule = headrule;
236 for (i=4; i<argc; i=i+5) {
237 if (! currentrule)
238 {
239 /* first element in the list */
240 currentrule = calloc(sizeof(rule_t),1);
241 headrule = currentrule;
242 }
243 else
244 {
245 /* follow-up element */
246 currentrule->next = calloc(sizeof(rule_t),1);
247 currentrule = currentrule->next;
248 }
249
250 if ( init_led(&(currentrule->led), argv[i]) )
251 return 1;
252
253 if ( sscanf(argv[i+1], "%d", &(currentrule->minq)) != 1 )
254 return 1;
255
256 if ( sscanf(argv[i+2], "%d", &(currentrule->maxq)) != 1 )
257 return 1;
258
259 if ( sscanf(argv[i+3], "%d", &(currentrule->boffset)) != 1 )
260 return 1;
261
262 if ( sscanf(argv[i+4], "%d", &(currentrule->bfactor)) != 1 )
263 return 1;
264 }
265 log_rules(headrule);
266
267 q0 = -1;
268 do {
269 q = quality(iw, ifname);
270 if ( q < q0 - s || q > q0 + s ) {
271 update_leds(headrule, q);
272 q0=q;
273 };
274 // re-open backend...
275 if ( q == -1 && q0 == -1 ) {
276 if (iw) {
277 iwinfo_finish();
278 iw=NULL;
279 usleep(BACKEND_RETRY_DELAY);
280 }
281 while (open_backend(&iw, ifname))
282 usleep(BACKEND_RETRY_DELAY);
283 }
284 usleep(r);
285 } while(1);
286
287 iwinfo_finish();
288
289 return 0;
290 }