1 /*
2  *  Hamlib Interface - provides registering for dynamically loadable backends.
3  *  Copyright (c) 2000-2015 by Stephane Fillod
4  *
5  *
6  *   This library is free software; you can redistribute it and/or
7  *   modify it under the terms of the GNU Lesser General Public
8  *   License as published by the Free Software Foundation; either
9  *   version 2.1 of the License, or (at your option) any later version.
10  *
11  *   This library is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *   Lesser General Public License for more details.
15  *
16  *   You should have received a copy of the GNU Lesser General Public
17  *   License along with this library; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21 
22 /**
23  * \brief Dynamic registration of backends
24  * \file register.c
25  *
26  * doc todo: Let's explain what's going on here!
27  */
28 
29 #ifdef HAVE_CONFIG_H
30 #  include "config.h"
31 #endif
32 
33 #include <errno.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <stdio.h>
38 #include <sys/types.h>
39 
40 #include <register.h>
41 
42 #include <hamlib/rig.h>
43 #include "misc.h"
44 
45 //! @cond Doxygen_Suppress
46 #ifndef PATH_MAX
47 #  define PATH_MAX       1024
48 #endif
49 
50 #define RIG_BACKEND_MAX 32
51 
52 #define DEFINE_INITRIG_BACKEND(backend) \
53     int MAKE_VERSIONED_FN(PREFIX_INITRIG, ABI_VERSION, backend(void *be_handle)); \
54     rig_model_t MAKE_VERSIONED_FN(PREFIX_PROBERIG, ABI_VERSION, backend(hamlib_port_t *port, rig_probe_func_t cfunc, rig_ptr_t data))
55 
56 #define RIG_FUNCNAMA(backend) MAKE_VERSIONED_FN(PREFIX_INITRIG, ABI_VERSION, backend)
57 #define RIG_FUNCNAMB(backend) MAKE_VERSIONED_FN(PREFIX_PROBERIG, ABI_VERSION, backend)
58 
59 #define RIG_FUNCNAM(backend) RIG_FUNCNAMA(backend),RIG_FUNCNAMB(backend)
60 
61 /*
62  * RIG_BACKEND_LIST is defined here, please keep it up to date,
63  *  i.e. each time you implement a new backend.
64  */
65 DEFINE_INITRIG_BACKEND(dummy);
66 DEFINE_INITRIG_BACKEND(yaesu);
67 DEFINE_INITRIG_BACKEND(kenwood);
68 DEFINE_INITRIG_BACKEND(icom);
69 DEFINE_INITRIG_BACKEND(icmarine);
70 DEFINE_INITRIG_BACKEND(pcr);
71 DEFINE_INITRIG_BACKEND(aor);
72 DEFINE_INITRIG_BACKEND(jrc);
73 DEFINE_INITRIG_BACKEND(uniden);
74 DEFINE_INITRIG_BACKEND(drake);
75 DEFINE_INITRIG_BACKEND(lowe);
76 DEFINE_INITRIG_BACKEND(racal);
77 DEFINE_INITRIG_BACKEND(wj);
78 DEFINE_INITRIG_BACKEND(skanti);
79 DEFINE_INITRIG_BACKEND(tentec);
80 DEFINE_INITRIG_BACKEND(alinco);
81 DEFINE_INITRIG_BACKEND(kachina);
82 DEFINE_INITRIG_BACKEND(tapr);
83 DEFINE_INITRIG_BACKEND(flexradio);
84 DEFINE_INITRIG_BACKEND(rft);
85 DEFINE_INITRIG_BACKEND(kit);
86 DEFINE_INITRIG_BACKEND(tuner);
87 DEFINE_INITRIG_BACKEND(rs);
88 DEFINE_INITRIG_BACKEND(prm80);
89 DEFINE_INITRIG_BACKEND(adat);
90 DEFINE_INITRIG_BACKEND(dorji);
91 DEFINE_INITRIG_BACKEND(barrett);
92 DEFINE_INITRIG_BACKEND(elad);
93 //! @endcond
94 
95 #ifdef HAVE_WINRADIO
96 //! @cond Doxygen_Suppress
97 DEFINE_INITRIG_BACKEND(winradio);
98 //! @endcond
99 #endif
100 
101 
102 /**
103  *  \def rig_backend_list
104  *  \brief Static list of rig models.
105  *
106  *  This is a NULL terminated list of available rig backends. Each entry in
107  *  the list consists of two fields: The branch number, which is an integer,
108  *  and the branch name, which is a character string.
109  */
110 static struct
111 {
112     int be_num;
113     const char *be_name;
114     int (* be_init_all)(void *handle);
115     rig_model_t (* be_probe_all)(hamlib_port_t *, rig_probe_func_t, rig_ptr_t);
116 } rig_backend_list[RIG_BACKEND_MAX] =
117 {
118     { RIG_DUMMY, RIG_BACKEND_DUMMY, RIG_FUNCNAMA(dummy) },
119     { RIG_YAESU, RIG_BACKEND_YAESU, RIG_FUNCNAM(yaesu) },
120     { RIG_KENWOOD, RIG_BACKEND_KENWOOD, RIG_FUNCNAM(kenwood) },
121     { RIG_ICOM, RIG_BACKEND_ICOM, RIG_FUNCNAM(icom) },
122     { RIG_ICMARINE, RIG_BACKEND_ICMARINE, RIG_FUNCNAMA(icmarine) },
123     { RIG_PCR, RIG_BACKEND_PCR, RIG_FUNCNAMA(pcr) },
124     { RIG_AOR, RIG_BACKEND_AOR, RIG_FUNCNAMA(aor) },
125     { RIG_JRC, RIG_BACKEND_JRC, RIG_FUNCNAMA(jrc) },
126     { RIG_UNIDEN, RIG_BACKEND_UNIDEN, RIG_FUNCNAM(uniden) },
127     { RIG_DRAKE, RIG_BACKEND_DRAKE, RIG_FUNCNAM(drake) },
128     { RIG_LOWE, RIG_BACKEND_LOWE, RIG_FUNCNAM(lowe) },
129     { RIG_RACAL, RIG_BACKEND_RACAL, RIG_FUNCNAMA(racal) },
130     { RIG_WJ, RIG_BACKEND_WJ, RIG_FUNCNAMA(wj) },
131     { RIG_SKANTI, RIG_BACKEND_SKANTI, RIG_FUNCNAMA(skanti) },
132 #ifdef HAVE_WINRADIO
133     { RIG_WINRADIO, RIG_BACKEND_WINRADIO, RIG_FUNCNAMA(winradio) },
134 #endif /* HAVE_WINRADIO */
135     { RIG_TENTEC, RIG_BACKEND_TENTEC, RIG_FUNCNAMA(tentec) },
136     { RIG_ALINCO, RIG_BACKEND_ALINCO, RIG_FUNCNAMA(alinco) },
137     { RIG_KACHINA, RIG_BACKEND_KACHINA, RIG_FUNCNAMA(kachina) },
138     { RIG_TAPR, RIG_BACKEND_TAPR, RIG_FUNCNAMA(tapr) },
139     { RIG_FLEXRADIO, RIG_BACKEND_FLEXRADIO, RIG_FUNCNAMA(flexradio) },
140     { RIG_RFT, RIG_BACKEND_RFT, RIG_FUNCNAMA(rft) },
141     { RIG_KIT, RIG_BACKEND_KIT, RIG_FUNCNAMA(kit) },
142     { RIG_TUNER, RIG_BACKEND_TUNER, RIG_FUNCNAMA(tuner) },
143     { RIG_RS, RIG_BACKEND_RS, RIG_FUNCNAMA(rs) },
144     { RIG_PRM80, RIG_BACKEND_PRM80, RIG_FUNCNAMA(prm80) },
145     { RIG_ADAT, RIG_BACKEND_ADAT, RIG_FUNCNAM(adat) },
146     { RIG_DORJI, RIG_BACKEND_DORJI, RIG_FUNCNAMA(dorji) },
147     { RIG_BARRETT, RIG_BACKEND_BARRETT, RIG_FUNCNAMA(barrett) },
148     { RIG_ELAD, RIG_BACKEND_ELAD, RIG_FUNCNAMA(elad) },
149     { 0, NULL }, /* end */
150 };
151 
152 
153 /*
154  * This struct to keep track of known rig models.
155  * It is chained, and used in a hash table, see below.
156  */
157 //! @cond Doxygen_Suppress
158 struct rig_list
159 {
160     const struct rig_caps *caps;
161     struct rig_list *next;
162 };
163 //! @endcond
164 
165 
166 // This size has to be > than the max# of rigs for any manufacturer
167 // A fatal error will occur when running rigctl if this value is too small
168 //! @cond Doxygen_Suppress
169 #define RIGLSTHASHSZ 65535
170 #define HASH_FUNC(a) ((a)%RIGLSTHASHSZ)
171 //! @endcond
172 
173 
174 /*
175  * The rig_hash_table is a hash table pointing to a list of next==NULL
176  *  terminated caps.
177  */
178 static struct rig_list *rig_hash_table[RIGLSTHASHSZ] = { NULL, };
179 
180 
181 static int rig_lookup_backend(rig_model_t rig_model);
182 
183 
184 /*
185  * Basically, this is a hash insert function that doesn't check for dup!
186  */
187 //! @cond Doxygen_Suppress
rig_register(const struct rig_caps * caps)188 int HAMLIB_API rig_register(const struct rig_caps *caps)
189 {
190     int hval;
191     struct rig_list *p;
192 
193     rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
194 
195     if (!caps)
196     {
197         return -RIG_EINVAL;
198     }
199 
200     rig_debug(RIG_DEBUG_VERBOSE,
201               "%s: rig_register (%u)\n",
202               __func__,
203               caps->rig_model);
204 
205     p = (struct rig_list *)malloc(sizeof(struct rig_list));
206 
207     if (!p)
208     {
209         return -RIG_ENOMEM;
210     }
211 
212     hval = HASH_FUNC(caps->rig_model);
213 
214     if (rig_hash_table[hval])
215     {
216         printf("Hash collision!!! Fatal error!!\n");
217         exit(1);
218     }
219 
220     p->caps = caps;
221     // p->handle = NULL;
222     p->next = rig_hash_table[hval];
223     rig_hash_table[hval] = p;
224 
225     RETURNFUNC(RIG_OK);
226 }
227 //! @endcond
228 
229 /*
230  * Get rig capabilities.
231  * ie. rig_hash_table lookup
232  */
233 
234 //! @cond Doxygen_Suppress
rig_get_caps(rig_model_t rig_model)235 const struct rig_caps *HAMLIB_API rig_get_caps(rig_model_t rig_model)
236 {
237     struct rig_list *p;
238 
239     for (p = rig_hash_table[HASH_FUNC(rig_model)]; p; p = p->next)
240     {
241         if (p->caps->rig_model == rig_model)
242         {
243             return p->caps;
244         }
245     }
246 
247     return NULL;    /* sorry, caps not registered! */
248 }
249 //! @endcond
250 
251 /*
252  * lookup for backend index in rig_backend_list table,
253  * according to BACKEND_NUM
254  * return -1 if not found.
255  */
256 //! @cond Doxygen_Suppress
rig_lookup_backend(rig_model_t rig_model)257 static int rig_lookup_backend(rig_model_t rig_model)
258 {
259     int i;
260 
261     for (i = 0; i < RIG_BACKEND_MAX && rig_backend_list[i].be_name; i++)
262     {
263         if (RIG_BACKEND_NUM(rig_model) ==
264                 rig_backend_list[i].be_num)
265 
266         {
267             return i;
268         }
269     }
270 
271     return -1;
272 }
273 //! @endcond
274 
275 /*
276  * rig_check_backend
277  * check the backend declaring this model has been loaded
278  * and if not loaded already, load it!
279  * This permits seamless operation in rig_init.
280  */
281 //! @cond Doxygen_Suppress
rig_check_backend(rig_model_t rig_model)282 int HAMLIB_API rig_check_backend(rig_model_t rig_model)
283 {
284     const struct rig_caps *caps;
285     int be_idx;
286     int retval;
287     int i, n;
288 
289     /* already loaded ? */
290     caps = rig_get_caps(rig_model);
291 
292     if (caps)
293     {
294         return RIG_OK;
295     }
296 
297     // hmmm...no caps so did we already load the rigs?
298     for (n = 0, i = 0; i < RIGLSTHASHSZ; i++)
299     {
300         if (rig_hash_table[i]) { ++n; }
301     }
302 
303     if (n > 1)
304     {
305         rig_debug(RIG_DEBUG_ERR, "%s: rig model %d not found and rig count=%d\n",
306                   __func__, rig_model, n);
307         return -RIG_ENAVAIL;
308     }
309 
310     be_idx = rig_lookup_backend(rig_model);
311 
312     /*
313      * Never heard about this backend family!
314      */
315     if (be_idx == -1)
316     {
317         rig_debug(RIG_DEBUG_VERBOSE,
318                   "rig_check_backend: unsupported backend %u for model %u\n",
319                   RIG_BACKEND_NUM(rig_model),
320                   rig_model);
321         return -RIG_ENAVAIL;
322     }
323 
324     // do we need to load the backend?
325 //    if (rig_backend_list[be_idx].be_init_all == 0)
326     {
327         retval = rig_load_backend(rig_backend_list[be_idx].be_name);
328     }
329 #if 0
330     else
331     {
332         retval = RIG_OK;
333     }
334 
335 #endif
336 
337     return retval;
338 }
339 //! @endcond
340 
341 
342 
343 //! @cond Doxygen_Suppress
rig_unregister(rig_model_t rig_model)344 int HAMLIB_API rig_unregister(rig_model_t rig_model)
345 {
346     int hval;
347     struct rig_list *p, *q;
348 
349     hval = HASH_FUNC(rig_model);
350     q = NULL;
351 
352     for (p = rig_hash_table[hval]; p; p = p->next)
353     {
354         if (p->caps->rig_model == rig_model)
355         {
356             if (q == NULL)
357             {
358                 rig_hash_table[hval] = p->next;
359             }
360             else
361             {
362                 q->next = p->next;
363             }
364 
365             free(p);
366             return RIG_OK;
367         }
368 
369         q = p;
370     }
371 
372     return -RIG_EINVAL; /* sorry, caps not registered! */
373 }
374 //! @endcond
375 
376 /*
377  * rig_list_foreach
378  * executes cfunc on all the elements stored in the rig hash list
379  */
380 //! @cond Doxygen_Suppress
rig_list_foreach(int (* cfunc)(const struct rig_caps *,rig_ptr_t),rig_ptr_t data)381 int HAMLIB_API rig_list_foreach(int (*cfunc)(const struct rig_caps *,
382                                 rig_ptr_t),
383                                 rig_ptr_t data)
384 {
385     struct rig_list *p;
386     int i;
387 
388     if (!cfunc)
389     {
390         return -RIG_EINVAL;
391     }
392 
393     for (i = 0; i < RIGLSTHASHSZ; i++)
394     {
395         struct rig_list *next = NULL;
396 
397         for (p = rig_hash_table[i]; p; p = next)
398         {
399             next = p->next;       /* read before call in case it is unregistered */
400 
401             if ((*cfunc)(p->caps, data) == 0)
402             {
403                 return RIG_OK;
404             }
405         }
406     }
407 
408     return RIG_OK;
409 }
410 //! @endcond
411 
412 /*
413  * rig_list_foreach_model
414  * executes cfunc on all the elements stored in the rig hash list
415  */
416 //! @cond Doxygen_Suppress
rig_list_foreach_model(int (* cfunc)(const rig_model_t rig_model,rig_ptr_t),rig_ptr_t data)417 int HAMLIB_API rig_list_foreach_model(int (*cfunc)(const rig_model_t rig_model,
418                                       rig_ptr_t),
419                                       rig_ptr_t data)
420 {
421     struct rig_list *p;
422     int i;
423 
424     if (!cfunc)
425     {
426         return -RIG_EINVAL;
427     }
428 
429     for (i = 0; i < RIGLSTHASHSZ; i++)
430     {
431         struct rig_list *next = NULL;
432 
433         for (p = rig_hash_table[i]; p; p = next)
434         {
435             next = p->next;       /* read before call in case it is unregistered */
436 
437             if ((*cfunc)(p->caps->rig_model, data) == 0)
438             {
439                 return RIG_OK;
440             }
441         }
442     }
443 
444     return RIG_OK;
445 }
446 //! @endcond
447 
448 //! @cond Doxygen_Suppress
dummy_rig_probe(const hamlib_port_t * p,rig_model_t model,rig_ptr_t data)449 static int dummy_rig_probe(const hamlib_port_t *p,
450                            rig_model_t model,
451                            rig_ptr_t data)
452 {
453     rig_debug(RIG_DEBUG_TRACE, "Found rig, model %u\n", model);
454 
455     return RIG_OK;
456 }
457 //! @endcond
458 
459 
460 /*
461  * rig_probe_first
462  * called straight by rig_probe
463  */
464 //! @cond Doxygen_Suppress
rig_probe_first(hamlib_port_t * p)465 rig_model_t rig_probe_first(hamlib_port_t *p)
466 {
467     int i;
468     rig_model_t model;
469 
470     for (i = 0; i < RIG_BACKEND_MAX && rig_backend_list[i].be_name; i++)
471     {
472         if (rig_backend_list[i].be_probe_all)
473         {
474             model = (*rig_backend_list[i].be_probe_all)(p, dummy_rig_probe,
475                     (rig_ptr_t)NULL);
476 
477             /* stop at first one found */
478             if (model != RIG_MODEL_NONE)
479             {
480                 return model;
481             }
482         }
483     }
484 
485     return RIG_MODEL_NONE;
486 }
487 //! @endcond
488 
489 
490 /*
491  * rig_probe_all_backends
492  * called straight by rig_probe_all
493  */
494 //! @cond Doxygen_Suppress
rig_probe_all_backends(hamlib_port_t * p,rig_probe_func_t cfunc,rig_ptr_t data)495 int rig_probe_all_backends(hamlib_port_t *p,
496                            rig_probe_func_t cfunc,
497                            rig_ptr_t data)
498 {
499     int i;
500 
501     for (i = 0; i < RIG_BACKEND_MAX && rig_backend_list[i].be_name; i++)
502     {
503         if (rig_backend_list[i].be_probe_all)
504         {
505             (*rig_backend_list[i].be_probe_all)(p, cfunc, data);
506         }
507     }
508 
509     return RIG_OK;
510 }
511 //! @endcond
512 
513 
514 //! @cond Doxygen_Suppress
rig_load_all_backends()515 int rig_load_all_backends()
516 {
517     int i;
518 
519     memset(rig_hash_table, 0, sizeof(rig_hash_table));
520 
521     for (i = 0; i < RIG_BACKEND_MAX && rig_backend_list[i].be_name; i++)
522     {
523         rig_load_backend(rig_backend_list[i].be_name);
524     }
525 
526     return RIG_OK;
527 }
528 //! @endcond
529 
530 
531 //! @cond Doxygen_Suppress
532 typedef int (*backend_init_t)(rig_ptr_t);
533 //! @endcond
534 
535 
536 /*
537  * rig_load_backend
538  */
539 //! @cond Doxygen_Suppress
rig_load_backend(const char * be_name)540 int HAMLIB_API rig_load_backend(const char *be_name)
541 {
542     int i;
543     backend_init_t be_init;
544 
545     for (i = 0; i < RIG_BACKEND_MAX && rig_backend_list[i].be_name; i++)
546     {
547         if (!strcmp(be_name, rig_backend_list[i].be_name))
548         {
549             be_init = rig_backend_list[i].be_init_all ;
550 
551             if (be_init)
552             {
553                 return (*be_init)(NULL);
554             }
555             else
556             {
557                 return -RIG_EINVAL;
558             }
559         }
560     }
561 
562     return -RIG_EINVAL;
563 }
564 //! @endcond
565