Document UUID dependency, make radio plugin buildable
[src/app-framework-binder.git] / src / radio-api.c
index 37da4eb..ef34504 100644 (file)
@@ -33,6 +33,7 @@ typedef enum { FM, AM } Mode;
 typedef struct dongle_ctx dongle_ctx;
 typedef struct demod_ctx demod_ctx;
 typedef struct output_ctx output_ctx;
+typedef struct dev_ctx dev_ctx_T;
 
 struct dongle_ctx {
     pthread_t thr;
@@ -66,6 +67,7 @@ struct output_ctx {
 };
 
 struct dev_ctx {
+    int used;  // radio is free ???
     rtlsdr_dev_t* dev;
     Mode mode;
     float freq;
@@ -78,32 +80,34 @@ struct dev_ctx {
 };
 
 
-void* _dongle_thread_fn (void *);
-void* _demod_thread_fn (void *);
-void* _output_thread_fn (void *);
-unsigned int _radio_dev_count (void);
-const char* _radio_dev_name (unsigned int);
-unsigned char _radio_dev_init (struct dev_ctx *, unsigned int);
-unsigned char _radio_dev_free (struct dev_ctx *);
-void _radio_apply_params (struct dev_ctx *);
-void _radio_start_threads (struct dev_ctx *);
-void _radio_stop_threads (struct dev_ctx *);
+STATIC void* _dongle_thread_fn (void *);
+STATIC void* _demod_thread_fn (void *);
+STATIC void* _output_thread_fn (void *);
+STATIC unsigned int _radio_dev_count (void);
+STATIC const char* _radio_dev_name (unsigned int);
+STATIC unsigned char _radio_dev_init (struct dev_ctx *, unsigned int);
+STATIC unsigned char _radio_dev_free (struct dev_ctx *);
+STATIC void _radio_apply_params (struct dev_ctx *);
+STATIC void _radio_start_threads (struct dev_ctx *);
+STATIC void _radio_stop_threads (struct dev_ctx *);
 
 static unsigned int init_dev_count;
 static struct dev_ctx **dev_ctx;
 
 /* ------------- RADIO IMPLEMENTATION ----------------- */
 
- /* ---- PUBLIC FUNCTIONS --- */
 
-void radio_on () {
+// Radio initialization should be done only when user start the radio and not at plugin initialization
+// Making this call too early would impose to restart the binder to detect a radio.
+STATIC void initRadio () {
     init_dev_count = _radio_dev_count();
     int i;
 
-    dev_ctx = (struct dev_ctx**) malloc(init_dev_count * sizeof(struct dev_ctx));
+    dev_ctx = (dev_ctx_T**) malloc(init_dev_count * sizeof(dev_ctx_T));
 
     for (i = 0; i < init_dev_count; i++) {
-        dev_ctx[i] = (struct dev_ctx*) malloc(sizeof(struct dev_ctx));
+        dev_ctx[i] = (dev_ctx_T*) malloc(sizeof(dev_ctx_T));
         dev_ctx[i]->dev = NULL;
         dev_ctx[i]->mode = FM;
         dev_ctx[i]->freq = 100.0;
@@ -116,7 +120,7 @@ void radio_on () {
     }
 }
 
-void radio_off () {
+STATIC void radio_off () {
     int i;
 
     for (i = 0; i < init_dev_count; i++) {
@@ -126,40 +130,40 @@ void radio_off () {
     free(dev_ctx);
 }
 
-void radio_set_mode (struct dev_ctx *dev_ctx, Mode mode) {
+STATIC void radio_set_mode (dev_ctx_T *dev_ctx, Mode mode) {
     dev_ctx->mode = mode;
     _radio_apply_params(dev_ctx);
 }
 
-void radio_set_freq (struct dev_ctx *dev_ctx, float freq) {
+STATIC void radio_set_freq (dev_ctx_T *dev_ctx, float freq) {
     dev_ctx->freq = freq;
     _radio_apply_params(dev_ctx);
 }
 
-void radio_set_mute (struct dev_ctx *dev_ctx, unsigned char mute) {
+STATIC void radio_set_mute (dev_ctx_T *dev_ctx, unsigned char mute) {
     dev_ctx->mute = mute;
     _radio_apply_params(dev_ctx);
 }
 
-void radio_play (struct dev_ctx *dev_ctx) {
+STATIC void radio_play (dev_ctx_T *dev_ctx) {
     _radio_start_threads(dev_ctx);
 }
 
-void radio_stop (struct dev_ctx *dev_ctx) {
+STATIC void radio_stop (dev_ctx_T *dev_ctx) {
     _radio_stop_threads(dev_ctx);
 }
 
  /* --- HELPER FUNCTIONS --- */
 
-unsigned int _radio_dev_count () {
+STATIC unsigned int _radio_dev_count () {
     return rtlsdr_get_device_count();
 }
 
-const char* _radio_dev_name (unsigned int num) {
+STATIC const char* _radio_dev_name (unsigned int num) {
     return rtlsdr_get_device_name(num);
 }
 
-unsigned char _radio_dev_init (struct dev_ctx *dev_ctx, unsigned int num) {
+STATIC unsigned char _radio_dev_init (dev_ctx_T *dev_ctx, unsigned int num) {
     rtlsdr_dev_t *dev = dev_ctx->dev;
 
     if (rtlsdr_open(&dev, num) < 0)
@@ -170,24 +174,26 @@ unsigned char _radio_dev_init (struct dev_ctx *dev_ctx, unsigned int num) {
     if (rtlsdr_reset_buffer(dev) < 0)
         return 0;
 
-    // dev_ctx->dev = dev; REQUIRED IN C TOO ? TEST !
+    dev_ctx->dev = dev;
 
     _radio_apply_params(dev_ctx);
 
     return 1;
 }
 
-unsigned char _radio_dev_free (struct dev_ctx *dev_ctx) {
+STATIC unsigned char _radio_dev_free (dev_ctx_T *dev_ctx) {
     rtlsdr_dev_t *dev = dev_ctx->dev;
 
     if (rtlsdr_close(dev) < 0)
         return 0;
     dev = NULL;
 
+    dev_ctx->dev = dev;
+
     return 1;
 }
 
-void _radio_apply_params (struct dev_ctx *dev_ctx) {
+STATIC void _radio_apply_params (dev_ctx_T *dev_ctx) {
     rtlsdr_dev_t *dev = dev_ctx->dev;
     Mode mode = dev_ctx->mode;
     float freq = dev_ctx->freq;
@@ -203,10 +209,10 @@ void _radio_apply_params (struct dev_ctx *dev_ctx) {
     rtlsdr_set_center_freq(dev, freq);
     rtlsdr_set_sample_rate(dev, rate);
 
-    // dev_ctx->dev = dev; REQUIRED IN C TOO ? TEST !
+    dev_ctx->dev = dev;
 }
 
-void _radio_start_threads (struct dev_ctx *dev_ctx) {
+STATIC void _radio_start_threads (dev_ctx_T *dev_ctx) {
     rtlsdr_dev_t *dev = dev_ctx->dev;
     dev_ctx->dongle = (dongle_ctx*) malloc(sizeof(dongle_ctx));
     dev_ctx->demod = (demod_ctx*) malloc(sizeof(demod_ctx));
@@ -241,7 +247,7 @@ void _radio_start_threads (struct dev_ctx *dev_ctx) {
     pthread_create(&output->thr, NULL, _output_thread_fn, (void*)dev_ctx);
 }
 
-void _radio_stop_threads (struct dev_ctx *dev_ctx) {
+STATIC void _radio_stop_threads (dev_ctx_T *dev_ctx) {
     rtlsdr_dev_t *dev = dev_ctx->dev;
     dongle_ctx *dongle = dev_ctx->dongle;
     demod_ctx *demod = dev_ctx->demod;
@@ -279,8 +285,8 @@ void _radio_stop_threads (struct dev_ctx *dev_ctx) {
 
  /* ---- LOCAL THREADED FUNCTIONS ---- */
 
-static void _rtlsdr_callback (unsigned char *buf, uint32_t len, void *ctx) {
-    struct dev_ctx *dev_ctx = (struct dev_ctx *)ctx;
+STATIC void _rtlsdr_callback (unsigned char *buf, uint32_t len, void *ctx) {
+    dev_ctx_T *dev_ctx = (dev_ctx_T *)ctx;
     dongle_ctx *dongle = dev_ctx->dongle;
     demod_ctx *demod = dev_ctx->demod;
     unsigned char tmp;
@@ -315,9 +321,9 @@ static void _rtlsdr_callback (unsigned char *buf, uint32_t len, void *ctx) {
        pthread_signal(&demod->ok, &demod->ok_m);
 }
  /**/
-void* _dongle_thread_fn (void *ctx) {
-    struct dev_ctx *dev_ctx = (struct dev_ctx *)ctx;
-    struct dongle_ctx *dongle = dev_ctx->dongle;
+STATIC void* _dongle_thread_fn (void *ctx) {
+    dev_ctx_T *dev_ctx = (dev_ctx_T *)ctx;
+    dongle_ctx *dongle = dev_ctx->dongle;
 
     rtlsdr_read_async(dev_ctx->dev, _rtlsdr_callback, dev_ctx, 0, 0);
 
@@ -325,7 +331,7 @@ void* _dongle_thread_fn (void *ctx) {
     return 0;
 }
 
-void _lowpass_demod (void *ctx) {
+STATIC void _lowpass_demod (void *ctx) {
     demod_ctx *demod = (demod_ctx *)ctx;
     int i=0, i2=0;
 
@@ -345,7 +351,7 @@ void _lowpass_demod (void *ctx) {
     demod->buf_len = i2;
 }
  /**/
-void _lowpassreal_demod (void *ctx) {
+STATIC void _lowpassreal_demod (void *ctx) {
     demod_ctx *demod = (demod_ctx *)ctx;
     int i=0, i2=0;
     int fast = 200000;
@@ -365,12 +371,12 @@ void _lowpassreal_demod (void *ctx) {
     demod->res_len = i2;
 }
  /**/
-void _multiply (int ar, int aj, int br, int bj, int *cr, int *cj) {
+STATIC void _multiply (int ar, int aj, int br, int bj, int *cr, int *cj) {
     *cr = ar*br - aj*bj;
     *cj = aj*br + ar*bj;
 }
  /**/
-int _polar_discriminant (int ar, int aj, int br, int bj) {
+STATIC int _polar_discriminant (int ar, int aj, int br, int bj) {
     int cr, cj;
     double angle;
     _multiply(ar, aj, br, -bj, &cr, &cj);
@@ -378,7 +384,7 @@ int _polar_discriminant (int ar, int aj, int br, int bj) {
     return (int)(angle / 3.14159 * (1<<14));
 }
  /**/
-void _fm_demod (void *ctx) {
+STATIC void _fm_demod (void *ctx) {
     demod_ctx *demod = (demod_ctx *)ctx;
     int16_t *buf = demod->buf;
     int buf_len = demod->buf_len;
@@ -396,7 +402,7 @@ void _fm_demod (void *ctx) {
     demod->res_len = buf_len/2;
 }
  /**/
-void _am_demod (void *ctx) {
+STATIC void _am_demod (void *ctx) {
     demod_ctx *demod = (demod_ctx *)ctx;
     int16_t *buf = demod->buf;
     int buf_len = demod->buf_len;
@@ -410,8 +416,8 @@ void _am_demod (void *ctx) {
     demod->res_len = buf_len/2;
 }
  /**/
-void* _demod_thread_fn (void *ctx) {
-    struct dev_ctx *dev_ctx = (struct dev_ctx *)ctx;
+STATIC void* _demod_thread_fn (void *ctx) {
+    dev_ctx_T *dev_ctx = (dev_ctx_T *)ctx;
     demod_ctx *demod = dev_ctx->demod;
     output_ctx *output = dev_ctx->output;
 
@@ -438,8 +444,8 @@ void* _demod_thread_fn (void *ctx) {
     return 0;
 }
 
-void* _output_thread_fn (void *ctx) {
-    struct dev_ctx *dev_ctx = (struct dev_ctx *)ctx;
+STATIC void* _output_thread_fn (void *ctx) {
+    dev_ctx_T *dev_ctx = (dev_ctx_T *)ctx;
     output_ctx *output = dev_ctx->output;
 
     while (dev_ctx->should_run) {
@@ -454,7 +460,7 @@ void* _output_thread_fn (void *ctx) {
     return 0;
 }
 
-/* -------------- PLUGIN BINDING ------------------- */
+
 
 STATIC json_object* start (AFB_session *session, AFB_request *request, void* handle) {
     json_object *response;
@@ -487,23 +493,171 @@ STATIC json_object* stop (AFB_session *session, AFB_request *request, void* hand
 }
 
 
-STATIC struct {
-    void * somedata;
-} handle;
+// ********************************************************
+
+// FULUP integration proposal with client session context
+
+// ********************************************************
+
+
+#define MAX_RADIO 10
+
+// Structure holding existing radio with current usage status
+typedef struct {
+    int   idx;
+    char *name;
+    int  used;
+} radioDevT;
+
+// Radio plugin handle should store everething API may need
+typedef struct {
+  radioDevT *radios[MAX_RADIO];  // pointer to existing radio
+  int devCount;
+} pluginHandleT;
+
+// Client Context Structure Hold any specific to client [will be destroyed when client leave]
+typedef struct {
+    dev_ctx_T radio;       // pointer to client radio
+    int idx;               // index of radio within global array
+} ctxHandleT;
+
+
+// It his was not a demo only, it should be smarter to enable hot plug/unplug
+STATIC void updateRadioDevList(pluginHandleT *handle) {
+  int idx;  
+
+  // loop on existing radio if any
+  for (idx = 0; idx < _radio_dev_count(); idx++) {
+      if (idx == MAX_RADIO) break;
+      handle->radios[idx] = calloc(1, sizeof(radioDevT)); // use calloc to set used to FALSE
+      handle->radios[idx]->name = (char *) _radio_dev_name(idx); 
+  }
+  handle->devCount = _radio_dev_count();
+}
+
+
+// This is call at plugin load time [radio devices might still not be visible]
+STATIC pluginHandleT* initRadioPlugin() {
+
+  // Allocate Plugin handle  
+  pluginHandleT *handle = calloc (1,sizeof (pluginHandleT)); // init handle with zero
+
+  // Some initialization steps
+  updateRadioDevList(handle);
+
+  return (handle);
+}
+
+// Stop a radio free related ressource and make it avaliable for other clients
+STATIC AFB_error releaseRadio (pluginHandleT* handle, ctxHandleT *ctx) {
+    
+   // change radio status
+   (handle->radios[ctx->idx])->used = FALSE;
+
+   // stop related threads and free attached resources
+   radio_stop (&ctx->radio);
+
+   // May be some further cleanup ????
+
+   return (AFB_SUCCESS); // Could it fails ????
+}
+
+
+// Start a radio and reserve exclusive usage to requesting client
+STATIC ctxHandleT  *reserveRadio (pluginHandleT* handle) {
+    ctxHandleT *client;
+    int idx;
+    
+   // loop on existing radio if any
+    for (idx = 0; idx < _radio_dev_count(); idx++) {
+        if ((handle->radios[client->idx])->used = FALSE) break;
+    }
+    
+    // No avaliable radio return now
+    if (idx == MAX_RADIO) return (NULL);
+    
+   // Book radio
+   (handle->radios[client->idx])->used = TRUE;
+   
+   // create client handle 
+   client = calloc (1, sizeof (ctxHandleT));
+   
+   // stop related threads and free attached resources
+   _radio_start_threads (&client->radio);
+   
+   // May be some things to do ????
+   
+   
+   return (client);
+}
+
+// This is called when client session died [ex; client quit for more than 15mn]
+STATIC json_object* freeRadio () {
+    
+    //releaseRadio (client->handle, client);
+    //free (client);
+}
+
+
+STATIC json_object* powerOnOff (AFB_request *request) {
+    json_object *jresp;
+    AFB_clientCtx *client = request->client; // get client context from request
+   
+    // Make sure binder was started with client session
+    if (client == NULL) {
+        request->errcode=MHD_HTTP_FORBIDDEN;
+        return (jsonNewMessage(AFB_FAIL, "Radio binder need session [--token=xxxx]"));        
+    }
+     
+    // If we have a handle radio was on let power it down
+    if (client->ctx != NULL) {
+        dev_ctx_T *dev_ctx = (dev_ctx_T *)client->ctx;
+
+        releaseRadio (client->plugin->handle, client->ctx);  // poweroff client related radio
+        
+        jresp = json_object_new_object();
+        json_object_object_add(jresp, "power", json_object_new_string ("off"));        
+        return (jresp);
+    }
+        
+    // request a new client context token and check result 
+    if (AFB_UNAUTH == ctxTokenCreate (request)) {
+        request->errcode=MHD_HTTP_UNAUTHORIZED;
+        jresp= jsonNewMessage(AFB_FAIL, "You're not authorized to request a radio [make sure you have the right authentication token");
+        return (jresp);
+    }
+    
+    // Client is clean let's look it we have an avaliable radio to propose
+    
+    // make sure we have last hot plug dongle visible
+    updateRadioDevList (client->plugin->handle); 
+    
+    // get try to get an unused radio
+    client->ctx = reserveRadio (client->plugin->handle);  
+    if (client->ctx == NULL) {
+       return (jsonNewMessage(AFB_FAIL, "Sory No More Radio Avaliable")); 
+    }  
+    
+    // At this point we should have something to retreive radio status before last poweroff [but this is only a demonstrator]
+}
 
 
 STATIC  AFB_restapi pluginApis[]= {
-  {"start"    , (AFB_apiCB)start      , "Ping Application Framework", NULL},
-  {"stop"     , (AFB_apiCB)stop       , "Ping Application Framework", NULL},
-  {0,0,0}
+  {"power"  , (AFB_apiCB)powerOnOff , "Ping Application Framework"},
+  {"start"  , (AFB_apiCB)start      , "Ping Application Framework"},
+  {"stop"   , (AFB_apiCB)stop       , "Ping Application Framework"},
+  {NULL}
 };
 
 PUBLIC AFB_plugin *radioRegister (AFB_session *session) {
     AFB_plugin *plugin = malloc (sizeof (AFB_plugin));
-    plugin->type  = AFB_PLUGIN;
+    plugin->type  = AFB_PLUGIN_JSON;
     plugin->info  = "Application Framework Binder - Radio plugin";
     plugin->prefix  = "radio";
     plugin->apis  = pluginApis;
+    
+    plugin->handle = initRadioPlugin();
+    plugin->freeCtxCB = freeRadio;
 
     return (plugin);
 };