57e3872c0f6a3bebad928e04893307305f971bef
[src/app-framework-binder.git] / plugins / radio / radio-api.c
1 /*
2  * Copyright (C) 2015 "IoT.bzh"
3  * Author "Manuel Bachmann"
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 3 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
13  * GNU 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, see <http://www.gnu.org/licenses/>.
17  */
18
19
20 #include "local-def.h"
21
22 /* -------------- RADIO DEFINITIONS ------------------ */
23
24 #include <math.h>
25 #include <pthread.h>
26 #include <rtl-sdr.h>
27
28 #define pthread_signal(n, m) pthread_mutex_lock(m); pthread_cond_signal(n); pthread_mutex_unlock(m)
29 #define pthread_wait(n, m) pthread_mutex_lock(m); pthread_cond_wait(n, m); pthread_mutex_unlock(m)
30 #define BUF_LEN 16*16384
31
32 typedef enum { FM, AM } Mode;
33 typedef struct dongle_ctx dongle_ctx;
34 typedef struct demod_ctx demod_ctx;
35 typedef struct output_ctx output_ctx;
36 typedef struct dev_ctx dev_ctx_T;
37
38 struct dongle_ctx {
39     pthread_t thr;
40     unsigned char thr_finished;
41     uint16_t buf[BUF_LEN];
42     uint32_t buf_len;
43 };
44
45 struct demod_ctx {
46     pthread_t thr;
47     unsigned char thr_finished;
48     pthread_rwlock_t lck;
49     pthread_cond_t ok;
50     pthread_mutex_t ok_m;
51     int pre_r, pre_j, now_r, now_j, index;
52     int pre_index, now_index;
53     int16_t buf[BUF_LEN];
54     int buf_len;
55     int16_t res[BUF_LEN];
56     int res_len;
57 };
58
59 struct output_ctx {
60     pthread_t thr;
61     unsigned char thr_finished;
62     pthread_rwlock_t lck;
63     pthread_cond_t ok;
64     pthread_mutex_t ok_m;
65     int16_t buf[BUF_LEN];
66     int buf_len;
67 };
68
69 struct dev_ctx {
70     int used;  // radio is free ???
71     rtlsdr_dev_t* dev;
72     Mode mode;
73     float freq;
74     unsigned char mute;
75     unsigned char should_run;
76      /* thread contexts */
77     dongle_ctx *dongle;
78     demod_ctx *demod;
79     output_ctx *output;
80 };
81
82 #define MAX_RADIO 10
83
84 // Structure holding existing radio with current usage status
85 typedef struct {
86     int   idx;
87     char *name;
88     int  used;
89 } radioDevT;
90
91 // Radio plugin handle should store everething API may need
92 typedef struct {
93   radioDevT *radios[MAX_RADIO];  // pointer to existing radio
94   int devCount;
95 } pluginHandleT;
96
97 /* private client context [will be destroyed when client leaves] */
98 typedef struct {
99     radioDevT *radio;       /* pointer to client radio            */
100     int idx;                /* radio index within global array    */
101     Mode mode;              /* radio mode: AM/FM                  */
102     float freq;             /* radio frequency (Mhz)              */
103     unsigned char mute;     /* radio muted: 0(false)/1(true)      */
104 } radioCtxHandleT;
105
106
107 STATIC void* _dongle_thread_fn (void *);
108 STATIC void* _demod_thread_fn (void *);
109 STATIC void* _output_thread_fn (void *);
110 STATIC unsigned int _radio_dev_count (void);
111 STATIC const char* _radio_dev_name (unsigned int);
112 STATIC unsigned char _radio_dev_init (struct dev_ctx *, unsigned int);
113 STATIC unsigned char _radio_dev_free (struct dev_ctx *);
114 STATIC void _radio_apply_params (struct dev_ctx *);
115 STATIC void _radio_start_threads (struct dev_ctx *);
116 STATIC void _radio_stop_threads (struct dev_ctx *);
117
118 static unsigned int init_dev_count = 0;
119 static struct dev_ctx **dev_ctx = NULL;
120
121 /* ------------- RADIO IMPLEMENTATION ----------------- */
122
123
124 // Radio initialization should be done only when user start the radio and not at plugin initialization
125 // Making this call too early would impose to restart the binder to detect a radio.
126 STATIC unsigned char _radio_on (unsigned int num, radioCtxHandleT *ctx) {
127  
128     if (num >= _radio_dev_count())
129         return 0;
130     
131     if (init_dev_count < _radio_dev_count()) {
132         init_dev_count = _radio_dev_count();
133         dev_ctx = (dev_ctx_T**) realloc (dev_ctx, init_dev_count * sizeof(dev_ctx_T));           
134     }
135
136     dev_ctx[num] = (dev_ctx_T*) malloc (sizeof(dev_ctx_T));
137     dev_ctx[num]->dev = NULL;
138     dev_ctx[num]->mode = ctx->mode;
139     dev_ctx[num]->freq = ctx->freq;
140     dev_ctx[num]->mute = ctx->mute;
141     dev_ctx[num]->should_run = 0;
142     dev_ctx[num]->dongle = NULL;
143     dev_ctx[num]->demod = NULL;
144     dev_ctx[num]->output = NULL;
145     _radio_dev_init(dev_ctx[num], num);
146     
147     return 1;
148 }
149
150 STATIC void _radio_off (unsigned int num) {
151
152     if (num >= _radio_dev_count())
153         return;
154
155     if (dev_ctx[num]) {
156         _radio_dev_free(dev_ctx[num]);
157         free(dev_ctx[num]);
158     }
159     /* free(dev_ctx); */
160 }
161
162 STATIC void _radio_set_mode (unsigned int num, Mode mode) {
163     if (!dev_ctx || !dev_ctx[num])
164         return;
165
166     dev_ctx[num]->mode = mode;
167     _radio_apply_params(dev_ctx[num]);
168 }
169
170 STATIC void _radio_set_freq (unsigned int num, float freq) {
171     if (!dev_ctx || !dev_ctx[num])
172         return;
173
174     dev_ctx[num]->freq = freq;
175     _radio_apply_params(dev_ctx[num]);
176 }
177
178 STATIC void _radio_set_mute (unsigned int num, unsigned char mute) {
179     if (!dev_ctx || !dev_ctx[num])
180         return;
181
182     dev_ctx[num]->mute = mute;
183     _radio_apply_params(dev_ctx[num]);
184 }
185
186 STATIC void _radio_play (unsigned int num) {
187     if (!dev_ctx || !dev_ctx[num])
188         return;
189
190     _radio_start_threads(dev_ctx[num]);
191 }
192
193 STATIC void _radio_stop (unsigned int num) {
194     if (!dev_ctx || !dev_ctx[num])
195         return;
196
197     _radio_stop_threads(dev_ctx[num]);
198 }
199
200  /* --- HELPER FUNCTIONS --- */
201
202 STATIC unsigned int _radio_dev_count () {
203     return rtlsdr_get_device_count();
204 }
205
206 STATIC const char* _radio_dev_name (unsigned int num) {
207     return rtlsdr_get_device_name(num);
208 }
209
210 STATIC unsigned char _radio_dev_init (dev_ctx_T *dev_ctx, unsigned int num) {
211     rtlsdr_dev_t *dev = dev_ctx->dev;
212
213     if (rtlsdr_open(&dev, num) < 0)
214         return 0;
215
216     rtlsdr_set_tuner_gain_mode(dev, 0);
217
218     if (rtlsdr_reset_buffer(dev) < 0)
219         return 0;
220
221     dev_ctx->dev = dev;
222
223     _radio_apply_params(dev_ctx);
224
225     return 1;
226 }
227
228 STATIC unsigned char _radio_dev_free (dev_ctx_T *dev_ctx) {
229     rtlsdr_dev_t *dev = dev_ctx->dev;
230
231     if (rtlsdr_close(dev) < 0)
232         return 0;
233     dev = NULL;
234
235     dev_ctx->dev = dev;
236
237     return 1;
238 }
239
240 STATIC void _radio_apply_params (dev_ctx_T *dev_ctx) {
241     rtlsdr_dev_t *dev = dev_ctx->dev;
242     Mode mode = dev_ctx->mode;
243     float freq = dev_ctx->freq;
244     int rate;
245
246     freq *= 1000000;
247     rate = ((1000000 / 200000) + 1) * 200000;
248
249     if (mode == FM)
250         freq += 16000;
251     freq += rate / 4;
252
253     rtlsdr_set_center_freq(dev, freq);
254     rtlsdr_set_sample_rate(dev, rate);
255
256     dev_ctx->dev = dev;
257 }
258
259 STATIC void _radio_start_threads (dev_ctx_T *dev_ctx) {
260     rtlsdr_dev_t *dev = dev_ctx->dev;
261     dev_ctx->dongle = (dongle_ctx*) malloc(sizeof(dongle_ctx));
262     dev_ctx->demod = (demod_ctx*) malloc(sizeof(demod_ctx));
263     dev_ctx->output = (output_ctx*) malloc(sizeof(output_ctx));
264
265     dongle_ctx *dongle = dev_ctx->dongle;
266     demod_ctx *demod = dev_ctx->demod;
267     output_ctx *output = dev_ctx->output;
268
269     pthread_rwlock_init(&demod->lck, NULL);
270     pthread_cond_init(&demod->ok, NULL);
271     pthread_mutex_init(&demod->ok_m, NULL);
272     pthread_rwlock_init(&output->lck, NULL);
273     pthread_cond_init(&output->ok, NULL);
274     pthread_mutex_init(&output->ok_m, NULL);
275
276     dev_ctx->should_run = 1;
277
278      /* dongle thread */
279     dongle->thr_finished = 0;
280     pthread_create(&dongle->thr, NULL, _dongle_thread_fn, (void*)dev_ctx);
281
282      /* demod thread */
283     demod->pre_r = demod->pre_j = 0;
284     demod->now_r = demod->now_j = 0;
285     demod->index = demod->pre_index = demod->now_index = 0;
286     demod->thr_finished = 0;
287     pthread_create(&demod->thr, NULL, _demod_thread_fn, (void*)dev_ctx);
288
289      /* output thread */
290     output->thr_finished = 0;
291     pthread_create(&output->thr, NULL, _output_thread_fn, (void*)dev_ctx);
292 }
293
294 STATIC void _radio_stop_threads (dev_ctx_T *dev_ctx) {
295     rtlsdr_dev_t *dev = dev_ctx->dev;
296     dongle_ctx *dongle = dev_ctx->dongle;
297     demod_ctx *demod = dev_ctx->demod;
298     output_ctx *output = dev_ctx->output;
299
300     if (!dongle || !demod || !output)
301         return;
302
303      /* stop each "while" loop in threads */
304     dev_ctx->should_run = 0;
305
306     rtlsdr_cancel_async(dev);
307     pthread_signal(&demod->ok, &demod->ok_m);
308     pthread_signal(&output->ok, &output->ok_m);
309
310     while (!dongle->thr_finished ||
311            !demod->thr_finished ||
312            !output->thr_finished)
313         usleep(100000);
314
315     pthread_join(dongle->thr, NULL);
316     pthread_join(demod->thr, NULL);
317     pthread_join(output->thr, NULL);
318     pthread_rwlock_destroy(&demod->lck);
319     pthread_cond_destroy(&demod->ok);
320     pthread_mutex_destroy(&demod->ok_m);
321     pthread_rwlock_destroy(&output->lck);
322     pthread_cond_destroy(&output->ok);
323     pthread_mutex_destroy(&output->ok_m);
324
325     free(dongle); dev_ctx->dongle = NULL;
326     free(demod); dev_ctx->demod = NULL;
327     free(output); dev_ctx->output = NULL;
328 }
329
330  /* ---- LOCAL THREADED FUNCTIONS ---- */
331
332 STATIC void _rtlsdr_callback (unsigned char *buf, uint32_t len, void *ctx) {
333     dev_ctx_T *dev_ctx = (dev_ctx_T *)ctx;
334     dongle_ctx *dongle = dev_ctx->dongle;
335     demod_ctx *demod = dev_ctx->demod;
336     unsigned char tmp;
337     int i;
338
339     if (!dev_ctx->should_run)
340         return;
341
342      /* rotate 90° */
343     for (i = 0; i < (int)len; i += 8) {
344         tmp = 255 - buf[i+3];
345         buf[i+3] = buf[i+2];
346         buf[i+2] = tmp;
347
348         buf[i+4] = 255 - buf[i+4];
349         buf[i+5] = 255 - buf[i+5];
350
351         tmp = 255 - buf[i+6];
352         buf[i+6] = buf[i+7];
353         buf[i+7] = tmp;
354     }
355
356      /* write data */
357     for (i = 0; i < (int)len; i++)
358         dongle->buf[i] = (int16_t)buf[i] - 127;
359
360      /* lock demod thread, write to it, unlock */
361        pthread_rwlock_wrlock(&demod->lck);
362     memcpy(demod->buf, dongle->buf, 2 * len);
363     demod->buf_len = len;
364        pthread_rwlock_unlock(&demod->lck);
365        pthread_signal(&demod->ok, &demod->ok_m);
366 }
367  /**/
368 STATIC void* _dongle_thread_fn (void *ctx) {
369     dev_ctx_T *dev_ctx = (dev_ctx_T *)ctx;
370     dongle_ctx *dongle = dev_ctx->dongle;
371
372     rtlsdr_read_async(dev_ctx->dev, _rtlsdr_callback, dev_ctx, 0, 0);
373
374     dongle->thr_finished = 1;
375     return 0;
376 }
377
378 STATIC void _lowpass_demod (void *ctx) {
379     demod_ctx *demod = (demod_ctx *)ctx;
380     int i=0, i2=0;
381
382     while (i < demod->buf_len) {
383         demod->now_r += demod->buf[i];
384         demod->now_j += demod->buf[i+1];
385         i += 2;
386         demod->index++;
387         if (demod->index < ((1000000 / 200000) + 1))
388             continue;
389         demod->buf[i2] = demod->now_r;
390         demod->buf[i2+1] = demod->now_j;
391         demod->index = 0;
392         demod->now_r = demod->now_j = 0;
393         i2 += 2;
394     }
395     demod->buf_len = i2;
396 }
397  /**/
398 STATIC void _lowpassreal_demod (void *ctx) {
399     demod_ctx *demod = (demod_ctx *)ctx;
400     int i=0, i2=0;
401     int fast = 200000;
402     int slow = 48000;
403
404     while (i < demod->res_len) {
405         demod->now_index += demod->res[i];
406         i++;
407         demod->pre_index += slow;
408         if (demod->pre_index < fast)
409             continue;
410         demod->res[i2] = (int16_t)(demod->now_index / (fast/slow));
411         demod->pre_index -= fast;
412         demod->now_index = 0;
413         i2 += 1;
414     }
415     demod->res_len = i2;
416 }
417  /**/
418 STATIC void _multiply (int ar, int aj, int br, int bj, int *cr, int *cj) {
419     *cr = ar*br - aj*bj;
420     *cj = aj*br + ar*bj;
421 }
422  /**/
423 STATIC int _polar_discriminant (int ar, int aj, int br, int bj) {
424     int cr, cj;
425     double angle;
426     _multiply(ar, aj, br, -bj, &cr, &cj);
427     angle = atan2((double)cj, (double)cr);
428     return (int)(angle / 3.14159 * (1<<14));
429 }
430  /**/
431 STATIC void _fm_demod (void *ctx) {
432     demod_ctx *demod = (demod_ctx *)ctx;
433     int16_t *buf = demod->buf;
434     int buf_len = demod->buf_len;
435     int pcm, i;
436
437     pcm = _polar_discriminant(buf[0], buf[1], demod->pre_r, demod->pre_j);
438     demod->res[0] = (int16_t)pcm;
439
440     for (i = 2; i < (buf_len-1); i += 2) {
441         pcm = _polar_discriminant(buf[i], buf[i+1], buf[i-2], buf[i-1]);
442         demod->res[i/2] = (int16_t)pcm;
443     }
444     demod->pre_r = buf[buf_len - 2];
445     demod->pre_j = buf[buf_len - 1];
446     demod->res_len = buf_len/2;
447 }
448  /**/
449 STATIC void _am_demod (void *ctx) {
450     demod_ctx *demod = (demod_ctx *)ctx;
451     int16_t *buf = demod->buf;
452     int buf_len = demod->buf_len;
453     int pcm, i;
454
455     for (i = 0; i < buf_len; i += 2) {
456         pcm = buf[i] * buf[i];
457         pcm += buf[i+1] * buf[i+1];
458         demod->res[i/2] = (int16_t)sqrt(pcm);
459     }
460     demod->res_len = buf_len/2;
461 }
462  /**/
463 STATIC void* _demod_thread_fn (void *ctx) {
464     dev_ctx_T *dev_ctx = (dev_ctx_T *)ctx;
465     demod_ctx *demod = dev_ctx->demod;
466     output_ctx *output = dev_ctx->output;
467
468     while(dev_ctx->should_run) {
469             pthread_wait(&demod->ok, &demod->ok_m);
470             pthread_rwlock_wrlock(&demod->lck);
471         _lowpass_demod(demod);
472         if (dev_ctx->mode == FM)
473             _fm_demod(demod);
474         else
475             _am_demod(demod);
476         _lowpassreal_demod(demod);
477            pthread_rwlock_unlock(&demod->lck);
478
479          /* lock demod thread, write to it, unlock */
480            pthread_rwlock_wrlock(&output->lck);
481         memcpy(output->buf, demod->res, 2 * demod->res_len);
482         output->buf_len = demod->res_len;
483            pthread_rwlock_unlock(&output->lck);
484            pthread_signal(&output->ok, &output->ok_m);
485     }
486
487     demod->thr_finished = 1;
488     return 0;
489 }
490
491 STATIC void* _output_thread_fn (void *ctx) {
492     dev_ctx_T *dev_ctx = (dev_ctx_T *)ctx;
493     output_ctx *output = dev_ctx->output;
494
495     while (dev_ctx->should_run) {
496            pthread_wait(&output->ok, &output->ok_m);
497            pthread_rwlock_rdlock(&output->lck);
498         //if (!dev_ctx->mute)
499         //    mRadio->PlayAlsa((void*)&output->buf, output->buf_len);
500            pthread_rwlock_unlock(&output->lck);
501     }
502
503     output->thr_finished = 1;
504     return 0;
505 }
506
507
508 /* ********************************************************
509
510    FULUP integration proposal with client session context
511
512    ******************************************************** */
513
514 // It his was not a demo only, it should be smarter to enable hot plug/unplug
515 STATIC void updateRadioDevList(pluginHandleT *handle) {
516   int idx;  
517
518   // loop on existing radio if any
519   for (idx = 0; idx < _radio_dev_count(); idx++) {
520       if (idx == MAX_RADIO) break;
521       handle->radios[idx] = calloc(1, sizeof(radioDevT)); // use calloc to set used to FALSE
522       handle->radios[idx]->name = (char *) _radio_dev_name(idx); 
523   }
524   handle->devCount = _radio_dev_count();
525 }
526
527
528 /* global plugin context creation ; at loading time [radio devices might still not be visible] */
529 STATIC pluginHandleT* initRadioPlugin() {
530
531   pluginHandleT *handle;
532
533   handle = calloc (1, sizeof(pluginHandleT));
534   updateRadioDevList (handle);
535
536   return handle;
537 }
538
539
540 /* private client context creation ; default values */
541 STATIC radioCtxHandleT* initRadioCtx () {
542
543     radioCtxHandleT *ctx;
544
545     ctx = malloc (sizeof(radioCtxHandleT));
546     ctx->radio = NULL;
547     ctx->idx = -1;
548     ctx->mode = FM;
549     ctx->freq = 100.0;
550     ctx->mute = 0;
551
552     return ctx;
553 }
554
555
556 /* reserve a radio device to requesting client, start it */
557 STATIC AFB_error reserveRadio (pluginHandleT *handle, radioCtxHandleT *ctx) {
558     int idx;
559
560     /* loop on all devices, find an unused one */
561     for (idx = 0; idx < _radio_dev_count(); idx++) {
562         if (idx == MAX_RADIO) break;
563         if (handle->radios[idx]->used == FALSE) goto found_radio; /* found one */
564     }
565     return AFB_FAIL;
566
567    found_radio:
568     /* try to power it on, passing client context info such as frequency... */
569     _radio_on (idx, ctx);
570     /* TODO : try to re-iterate from the next ones if it failed ! */
571
572     /* globally mark it as reserved */
573     handle->radios[idx]->used = TRUE;
574
575     /* store relevant info to client context (direct pointer, index) */
576     ctx->radio = handle->radios[idx];
577     ctx->idx = idx;
578
579     return AFB_SUCCESS;
580 }
581
582 /* free a radio device from requesting client, stop it */
583 STATIC AFB_error releaseRadio (pluginHandleT *handle, radioCtxHandleT *ctx) {
584
585    /* globally mark it as free */
586    handle->radios[ctx->idx]->used = FALSE;
587
588    /* power it off */
589    _radio_off (ctx->idx);
590
591    return AFB_SUCCESS;
592 }
593
594 // This is called when client session died [ex; client quit for more than 15mn]
595 STATIC json_object* freeRadio () {
596     
597     //releaseRadio (client->handle, client);
598     //free (client);
599 }
600
601
602 STATIC json_object* power (AFB_request *request) {      /* AFB_SESSION_CREATE */
603     
604     pluginHandleT *handle = request->client->plugin->handle; 
605     radioCtxHandleT *ctx = (radioCtxHandleT*)request->client->ctx;
606     const char *value = getQueryValue (request, "value");
607     json_object *jresp;
608
609     /* create a private client context if needed */
610     if (!ctx) ctx = initRadioCtx();
611
612     /* no "?value=" parameter : return current state */
613     if (!value) {
614         jresp = json_object_new_object();
615         ctx->radio ?
616             json_object_object_add (jresp, "power", json_object_new_string ("on"))
617           : json_object_object_add (jresp, "power", json_object_new_string ("off"));
618     }
619
620     /* "?value=" parameter is "1" or "on" */
621     else if ( atoi(value) == 1 || !strcasecmp(value, "on") ) {
622         if (!ctx->radio) {
623             if (reserveRadio (handle, ctx) == AFB_FAIL) {
624                 request->errcode = MHD_HTTP_SERVICE_UNAVAILABLE;
625                 return (jsonNewMessage (AFB_FAIL, "No more radio devices available"));
626             }
627         }
628         jresp = json_object_new_object();
629         json_object_object_add (jresp, "power-on", json_object_new_string ("ok"));
630     }
631
632     /* "?value=" parameter is "0" or "off" */
633     else if ( atoi(value) == 0 || !strcasecmp(value, "off") ) {
634         if (ctx->radio) {
635             if (releaseRadio (handle, ctx) == AFB_FAIL) {
636                 request->errcode = MHD_HTTP_SERVICE_UNAVAILABLE;
637                 return (jsonNewMessage (AFB_FAIL, "Unable to release radio device"));
638             }
639         }
640         jresp = json_object_new_object();
641         json_object_object_add (jresp, "power-off", json_object_new_string ("ok"));
642     }
643
644     return jresp;
645 }
646
647 STATIC json_object* mode (AFB_request *request) {        /* AFB_SESSION_CHECK */
648
649     radioCtxHandleT *ctx = (radioCtxHandleT*)request->client->ctx;
650     const char *value = getQueryValue (request, "value");
651     json_object *jresp;
652     char *mode_str;
653
654     /* no "?value=" parameter : return current state */
655     if (!value) {
656         jresp = json_object_new_object();
657         ctx->mode ?
658             json_object_object_add (jresp, "mode", json_object_new_string ("AM"))
659           : json_object_object_add (jresp, "mode", json_object_new_string ("FM"));
660     }
661
662     /* "?value=" parameter is "1" or "on" */
663     else if ( atoi(value) == 1 || !strcasecmp(value, "AM") ) {
664         mode_str = strdup ("mode-AM");
665         ctx->mode = AM;
666     }
667
668     /* "?value=" parameter is "0" or "off" */
669     else if ( atoi(value) == 0 || !strcasecmp(value, "FM") ) {
670         mode_str = strdup ("mode-FM");
671         ctx->mode = FM;
672     }
673
674     else {
675         request->errcode = MHD_HTTP_SERVICE_UNAVAILABLE;
676         return (jsonNewMessage (AFB_FAIL, "Invalid value for mode"));
677     }
678        
679     _radio_set_mode (ctx->idx, ctx->mode);
680         
681     jresp = json_object_new_object();
682     json_object_object_add (jresp, mode_str, json_object_new_string ("ok"));
683     
684     return jresp;
685 }
686
687 STATIC json_object* freq (AFB_request *request) {        /* AFB_SESSION_CHECK */
688
689     radioCtxHandleT *ctx = (radioCtxHandleT*)request->client->ctx;
690     const char *value = getQueryValue (request, "value");
691     json_object *jresp = json_object_new_object();
692     char *freq_str;
693
694     /* no "?value=" parameter : return current state */
695     if (!value) {
696         asprintf (&freq_str, "%f", ctx->freq);
697         json_object_object_add (jresp, "freq", json_object_new_string (freq_str));
698     }
699
700     /* "?value=" parameter, set frequency */
701     else {
702         ctx->freq = strtof(value, NULL);
703         _radio_set_freq (ctx->idx, ctx->freq);
704         
705         asprintf (&freq_str, "freq-%f", ctx->freq);
706         json_object_object_add (jresp, freq_str, json_object_new_string ("ok"));
707     }
708     
709     return jresp;
710 }
711
712 STATIC json_object* mute (AFB_request *request) {        /* AFB_SESSION_CHECK */
713
714     radioCtxHandleT *ctx = (radioCtxHandleT*)request->client->ctx;
715     const char *value = getQueryValue (request, "value");
716     json_object *jresp;
717     char *mute_str;
718
719     /* no "?value=" parameter : return current state */
720     if (!value) {
721         asprintf (&mute_str, "%d", ctx->mute);
722         jresp = json_object_new_object();
723         json_object_object_add (jresp, "mute", json_object_new_string (mute_str));
724     }
725
726     /* "?value=" parameter is "1" or "on" */
727     else if ( atoi(value) == 1 || !strcasecmp(value, "on") )
728         ctx->mute = 1;
729
730     /* "?value=" parameter is "0" or "off" */
731     else if ( atoi(value) == 0 || !strcasecmp(value, "off") )
732         ctx->mute = 0;
733         
734     else {
735         request->errcode = MHD_HTTP_SERVICE_UNAVAILABLE;
736         return (jsonNewMessage (AFB_FAIL, "Invalid value for mute"));
737     }
738        
739     _radio_set_mute (ctx->idx, ctx->mute);
740         
741     asprintf (&mute_str, "mute-%d", ctx->mute);
742     jresp = json_object_new_object();
743     json_object_object_add (jresp, mute_str, json_object_new_string ("ok"));
744     
745     return jresp;
746 }
747
748 STATIC json_object* play (AFB_request *request) {        /* AFB_SESSION_CHECK */
749
750     radioCtxHandleT *ctx = (radioCtxHandleT*)request->client->ctx;
751     const char *value = getQueryValue (request, "value");
752     json_object *jresp;
753     
754     if (!ctx->radio) {
755         request->errcode = MHD_HTTP_SERVICE_UNAVAILABLE;
756         return (jsonNewMessage (AFB_FAIL, "Radio device not powered on"));
757     }
758
759     /* "?value=" parameter is "1" or "on" */
760     else if ( atoi(value) == 1 || !strcasecmp(value, "on") ) {
761         /* radio playback */
762         _radio_play (ctx->idx);
763         jresp = json_object_new_object();
764         json_object_object_add (jresp, "play-on", json_object_new_string ("ok"));
765     }
766
767     /* "?value=" parameter is "0" or "off" */
768     else if ( atoi(value) == 0 || !strcasecmp(value, "off") ) {
769         /* radio stop */
770         _radio_stop (ctx->idx);
771         jresp = json_object_new_object();
772         json_object_object_add (jresp, "play-on", json_object_new_string ("ok"));
773     }
774
775     return jresp;
776 }
777
778 STATIC json_object* status (AFB_request *request) {
779     return NULL;
780 }
781
782
783 STATIC AFB_restapi pluginApis[]= {
784   {"power"  , AFB_SESSION_CREATE, (AFB_apiCB)power      , "Radio API - power"},
785   {"mode"   , AFB_SESSION_CHECK,  (AFB_apiCB)mode       , "Radio API - mode"},
786   {"freq"   , AFB_SESSION_CHECK,  (AFB_apiCB)freq       , "Radio API - freq"},
787   {"mute"   , AFB_SESSION_CHECK,  (AFB_apiCB)mute       , "Radio API - mute"},
788   {"play"   , AFB_SESSION_CHECK,  (AFB_apiCB)play       , "Radio API - play"},
789   {"status" , AFB_SESSION_RENEW,  (AFB_apiCB)status     , "Radio API - status"},
790   {NULL}
791 };
792
793 PUBLIC AFB_plugin* radioRegister (AFB_session *session) {
794     AFB_plugin *plugin = malloc (sizeof(AFB_plugin));
795     plugin->type  = AFB_PLUGIN_JSON;
796     plugin->info  = "Application Framework Binder - Radio plugin";
797     plugin->prefix  = "radio";
798     plugin->apis  = pluginApis;
799
800     plugin->handle = initRadioPlugin();
801     plugin->freeCtxCB = freeRadio;
802
803     return (plugin);
804 };