Logo Search packages:      
Sourcecode: hamlib version File versions  Download package

register.c

Go to the documentation of this file.
/*
 *  Hamlib Interface - provides registering for dynamically loadable backends.
 *  Copyright (c) 2000-2005 by Stephane Fillod
 *
 *    $Id: register.c,v 1.24 2006-10-15 00:27:51 aa6e Exp $
 *
 *   This library is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Library General Public License as
 *   published by the Free Software Foundation; either version 2 of
 *   the License, or (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this library; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/**
 * \brief Dynamic registration of backends
 * \file register.c
 *
 * doc todo: Let's explain what's going on here!
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>

/* This is libtool's dl wrapper */
#include <ltdl.h>

#include <hamlib/rig.h>

#ifndef PATH_MAX
# define PATH_MAX       1024
#endif

#define RIG_BACKEND_MAX 32

/*
 * RIG_BACKEND_LIST is defined in riglist.h, please keep it up to data,
 *    ie. each time you give birth to a new backend
 * Also, it should be possible to register "external" backend,
 * that is backend that were not known by Hamlib at compile time.
 * Maybe, riglist.h should reserve some numbers for them? --SF
 */
static struct {
      int be_num;
      const char *be_name;
      rig_model_t (* be_probe_all)(hamlib_port_t*, rig_probe_func_t, rig_ptr_t);
} rig_backend_list[RIG_BACKEND_MAX] = RIG_BACKEND_LIST;

/*
 * This struct to keep track of known rig models.
 * It is chained, and used in a hash table, see below.
 */
struct rig_list {
      const struct rig_caps *caps;
      lt_dlhandle handle;                 /* handle returned by lt_dlopen() */
      struct rig_list *next;
};

#define RIGLSTHASHSZ 16
#define HASH_FUNC(a) ((a)%RIGLSTHASHSZ)

/*
 * The rig_hash_table is a hash table pointing to a list of next==NULL
 *    terminated caps.
 */
static struct rig_list *rig_hash_table[RIGLSTHASHSZ] = { NULL, };


static int rig_lookup_backend(rig_model_t rig_model);

/*
 * Basically, this is a hash insert function that doesn't check for dup!
 */
int HAMLIB_API rig_register(const struct rig_caps *caps)
{
      int hval;
      struct rig_list *p;

      if (!caps)
            return -RIG_EINVAL;

      rig_debug(RIG_DEBUG_VERBOSE, "rig_register (%d)\n",caps->rig_model);

#ifndef DONT_WANT_DUP_CHECK
      if (rig_get_caps(caps->rig_model)!=NULL)
            return -RIG_EINVAL;
#endif

      p = (struct rig_list*)malloc(sizeof(struct rig_list));
      if (!p)
            return -RIG_ENOMEM;

      hval = HASH_FUNC(caps->rig_model);
      p->caps = caps;
      p->handle = NULL;
      p->next = rig_hash_table[hval];
      rig_hash_table[hval] = p;

      return RIG_OK;
}

/*
 * Get rig capabilities.
 * ie. rig_hash_table lookup
 */

const struct rig_caps * HAMLIB_API rig_get_caps(rig_model_t rig_model)
{
      struct rig_list *p;

      for (p = rig_hash_table[HASH_FUNC(rig_model)]; p; p=p->next) {
            if (p->caps->rig_model == rig_model)
                  return p->caps;
      }

      return NULL;      /* sorry, caps not registered! */
}

/*
 * lookup for backend index in rig_backend_list table,
 * according to BACKEND_NUM
 * return -1 if not found.
 */
static int rig_lookup_backend(rig_model_t rig_model)
{
      int i;

      for (i=0; i<RIG_BACKEND_MAX && rig_backend_list[i].be_name; i++) {
            if (RIG_BACKEND_NUM(rig_model) == 
                              rig_backend_list[i].be_num)
                  return i;
      }

      return -1;
}

/*
 * rig_check_backend
 * check the backend declaring this model has been loaded
 * and if not loaded already, load it!
 * This permits seamless operation in rig_init.
 */
int HAMLIB_API rig_check_backend(rig_model_t rig_model)
{
      const struct rig_caps *caps;
      int be_idx;
      int retval;
      
      /* already loaded ? */
      caps = rig_get_caps(rig_model);
      if (caps)
            return RIG_OK;

      be_idx = rig_lookup_backend(rig_model);

      /*
       * Never heard about this backend family!
       */
      if (be_idx == -1) {
            rig_debug(RIG_DEBUG_VERBOSE, "rig_check_backend: unsupported "
                              "backend %d for model %d\n", 
                              RIG_BACKEND_NUM(rig_model), rig_model);
            return -RIG_ENAVAIL;
      }

      retval = rig_load_backend(rig_backend_list[be_idx].be_name);

      return retval;
}



int HAMLIB_API rig_unregister(rig_model_t rig_model)
{
      int hval;
      struct rig_list *p,*q;

      hval = HASH_FUNC(rig_model);
      q = NULL;
      for (p = rig_hash_table[hval]; p; p=p->next) {
            if (p->caps->rig_model == rig_model) {
                  if (q == NULL)
                        rig_hash_table[hval] = p->next;
                  else
                        q->next = p->next;

                  free(p);
                  return RIG_OK;
            }
            q = p;
      }

      return -RIG_EINVAL;     /* sorry, caps not registered! */
}

/*
 * rig_list_foreach
 * executes cfunc on all the elements stored in the rig hash list
 */
int HAMLIB_API rig_list_foreach(int (*cfunc)(const struct rig_caps*, rig_ptr_t),rig_ptr_t data)
{
      struct rig_list *p;
      int i;

      if (!cfunc)
            return -RIG_EINVAL;

      for (i=0; i<RIGLSTHASHSZ; i++) {
            for (p=rig_hash_table[i]; p; p=p->next)
                  if ((*cfunc)(p->caps,data) == 0)
                        return RIG_OK;
      }

      return RIG_OK;
}

static int dummy_rig_probe(const hamlib_port_t *p, rig_model_t model, rig_ptr_t data)
{
      rig_debug(RIG_DEBUG_TRACE, "Found rig, model %d\n", model);
      return RIG_OK;
}

/*
 * rig_probe_first
 * called straight by rig_probe
 */
rig_model_t rig_probe_first(hamlib_port_t *p)
{
      int i;
      rig_model_t model;

      for (i=0; i<RIG_BACKEND_MAX && rig_backend_list[i].be_name; i++) {
            if (rig_backend_list[i].be_probe_all) {
                  model = (*rig_backend_list[i].be_probe_all)(p, dummy_rig_probe, (rig_ptr_t)NULL);
                  /* stop at first one found */
                  if (model != RIG_MODEL_NONE)
                        return model;
            }
      }
      return RIG_MODEL_NONE;
}

/*
 * rig_probe_all_backends
 * called straight by rig_probe_all
 */
int rig_probe_all_backends(hamlib_port_t *p, rig_probe_func_t cfunc, rig_ptr_t data)
{
      int i;

      for (i=0; i<RIG_BACKEND_MAX && rig_backend_list[i].be_name; i++) {
            if (rig_backend_list[i].be_probe_all) {
                  (*rig_backend_list[i].be_probe_all)(p, cfunc, data);
            }
      }
      return RIG_OK;
}


int rig_load_all_backends()
{
      int i;

      for (i=0; i<RIG_BACKEND_MAX && rig_backend_list[i].be_name; i++) {
                  rig_load_backend(rig_backend_list[i].be_name);
      }

      return RIG_OK;
}


#define MAXFUNCNAMELEN 64
typedef int (*backend_init_t)(rig_ptr_t);

/*
 * rig_check_backend_version
 * Check that the versioned init function name for be_name is the correct version.
 */
static int rig_check_backend_version(const lt_dlhandle be_handle, const char *be_name,
                                    backend_init_t *be_init)
{
      char initfname[MAXFUNCNAMELEN];

      snprintf(initfname, MAXFUNCNAMELEN, "initrigs%d_%s", ABI_VERSION, be_name);
      *be_init = (backend_init_t) lt_dlsym(be_handle, initfname);
      if (!*be_init) {
            rig_debug(RIG_DEBUG_ERR, "rig:  dlsym(%s) failed (%s)\n",
                              initfname, lt_dlerror());
            return -RIG_EINVAL;
      }

      return RIG_OK;
}

/*
 * rig_load_backend
 * Dynamically load a rig backend through dlopen mechanism
 */
int HAMLIB_API rig_load_backend(const char *be_name)
{
# define PREFIX "hamlib-"

      lt_dlhandle be_handle;
      backend_init_t be_init;
      int status;
      char libname[PATH_MAX];
      char probefname[MAXFUNCNAMELEN];
      int i;

      /*
       * lt_dlinit may be called several times
       *
       * FIXME: make static build seamless
       */
#if 0
      LTDL_SET_PRELOADED_SYMBOLS();
#endif

      status = lt_dlinit();
      if (status) {
            rig_debug(RIG_DEBUG_ERR, "rig_backend_load: lt_dlinit for %s "
                                          "failed: %s\n", be_name, lt_dlerror());
            return -RIG_EINTERNAL;
      }

      lt_dladdsearchdir(HAMLIB_MODULE_DIR);

      rig_debug(RIG_DEBUG_VERBOSE, "rig: loading backend %s\n",be_name);

      /*
       * add hamlib directory here
       */
      snprintf (libname, sizeof (libname), PREFIX"%s", be_name);

      be_handle = lt_dlopenext (libname);

      /*
       * external module not found? try dlopenself for backends 
       * compiled in static
       */
      if (!be_handle || rig_check_backend_version(be_handle, be_name, &be_init) != RIG_OK) {
            rig_debug(RIG_DEBUG_VERBOSE, "rig:  lt_dlopen(\"%s\") failed (%s), "
                                    "trying static symbols...\n",
                                    libname, lt_dlerror());
            be_handle = lt_dlopen (NULL);
            if (!be_handle || rig_check_backend_version(be_handle, be_name, &be_init) != RIG_OK) {
                  rig_debug(RIG_DEBUG_ERR, "rig:  lt_dlopen(\"%s\") failed (%s)\n",
                                    libname, lt_dlerror());
                  return -RIG_EINVAL;
            }
      }


      /*
       * register probe function if present
       * NOTE: rig_load_backend might have been called upon a backend
       *    not in riglist.h! In this case, do nothing.
       */
      for (i=0; i<RIG_BACKEND_MAX && rig_backend_list[i].be_name; i++) {
            if (!strncmp(be_name, rig_backend_list[i].be_name, 64)) {
                  snprintf(probefname, MAXFUNCNAMELEN, "probeallrigs%d_%s", ABI_VERSION, be_name);
                  rig_backend_list[i].be_probe_all = 
                        (rig_model_t (*)(hamlib_port_t*, rig_probe_func_t, rig_ptr_t))
                                    lt_dlsym (be_handle, probefname);
                        break;
            }
      }

      status = (*be_init)(be_handle);

      return status;
}


Generated by  Doxygen 1.6.0   Back to index