1 /*
2  *  Hamlib Interface - provides registering for dynamically loadable backends.
3  *  Copyright (c) 2000-2004 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 rotator backends
24  * \file rot_reg.c
25  *
26  * Similar to register.c
27  * doc todo: Let's explain what's going on here!
28  */
29 
30 #ifdef HAVE_CONFIG_H
31 #  include "config.h"
32 #endif
33 
34 #include <errno.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <stdio.h>
39 #include <sys/types.h>
40 
41 #include <hamlib/rotator.h>
42 
43 #include "register.h"
44 
45 //! @cond Doxygen_Suppress
46 #ifndef PATH_MAX
47 # define PATH_MAX       1024
48 #endif
49 
50 #define ROT_BACKEND_MAX 32
51 
52 #define DEFINE_INITROT_BACKEND(backend)                                 \
53     int MAKE_VERSIONED_FN(PREFIX_INITROTS,                              \
54                           ABI_VERSION,                                  \
55                           backend(void *be_handle));                    \
56     rig_model_t MAKE_VERSIONED_FN(PREFIX_PROBEROTS,                     \
57                                   ABI_VERSION,                          \
58                                   backend(hamlib_port_t *port,          \
59                                           rig_probe_func_t cfunc,       \
60                                           rig_ptr_t data))
61 
62 #define ROT_FUNCNAMA(backend) MAKE_VERSIONED_FN(PREFIX_INITROTS, ABI_VERSION, backend)
63 #define ROT_FUNCNAMB(backend) MAKE_VERSIONED_FN(PREFIX_PROBEROTS, ABI_VERSION, backend)
64 
65 #define ROT_FUNCNAM(backend) ROT_FUNCNAMA(backend),ROT_FUNCNAMB(backend)
66 
67 
68 DEFINE_INITROT_BACKEND(dummy);
69 DEFINE_INITROT_BACKEND(easycomm);
70 DEFINE_INITROT_BACKEND(fodtrack);
71 DEFINE_INITROT_BACKEND(rotorez);
72 DEFINE_INITROT_BACKEND(sartek);
73 DEFINE_INITROT_BACKEND(gs232a);
74 DEFINE_INITROT_BACKEND(kit);
75 DEFINE_INITROT_BACKEND(heathkit);
76 DEFINE_INITROT_BACKEND(spid);
77 DEFINE_INITROT_BACKEND(m2);
78 DEFINE_INITROT_BACKEND(ars);
79 DEFINE_INITROT_BACKEND(amsat);
80 DEFINE_INITROT_BACKEND(ts7400);
81 DEFINE_INITROT_BACKEND(celestron);
82 DEFINE_INITROT_BACKEND(ether6);
83 DEFINE_INITROT_BACKEND(cnctrk);
84 DEFINE_INITROT_BACKEND(prosistel);
85 DEFINE_INITROT_BACKEND(meade);
86 DEFINE_INITROT_BACKEND(ioptron);
87 DEFINE_INITROT_BACKEND(satel);
88 DEFINE_INITROT_BACKEND(radant);
89 #if HAVE_LIBINDI
90 DEFINE_INITROT_BACKEND(indi);
91 #endif
92 //! @endcond
93 
94 /**
95  *  \def rot_backend_list
96  *  \brief Static list of rotator models.
97  *
98  *  This is a NULL terminated list of available rotator backends. Each entry
99  *  in the list consists of two fields: The branch number, which is an integer,
100  *  and the branch name, which is a character string.
101  *  An external library, loaded dynamically, could add its own functions
102  *  pointers in this array.
103  */
104 static struct
105 {
106     int be_num;
107     const char *be_name;
108     int (*be_init)(void *);
109     rot_model_t (*be_probe)(hamlib_port_t *);
110 } rot_backend_list[ROT_BACKEND_MAX] =
111 {
112     { ROT_DUMMY, ROT_BACKEND_DUMMY, ROT_FUNCNAMA(dummy) },
113     { ROT_EASYCOMM, ROT_BACKEND_EASYCOMM, ROT_FUNCNAMA(easycomm) },
114     { ROT_FODTRACK, ROT_BACKEND_FODTRACK, ROT_FUNCNAMA(fodtrack) },
115     { ROT_ROTOREZ, ROT_BACKEND_ROTOREZ, ROT_FUNCNAMA(rotorez) },
116     { ROT_SARTEK, ROT_BACKEND_SARTEK, ROT_FUNCNAMA(sartek) },
117     { ROT_GS232A, ROT_BACKEND_GS232A, ROT_FUNCNAMA(gs232a) },
118     { ROT_KIT, ROT_BACKEND_KIT, ROT_FUNCNAMA(kit) },
119     { ROT_HEATHKIT, ROT_BACKEND_HEATHKIT, ROT_FUNCNAMA(heathkit) },
120     { ROT_SPID, ROT_BACKEND_SPID, ROT_FUNCNAMA(spid) },
121     { ROT_M2, ROT_BACKEND_M2, ROT_FUNCNAMA(m2) },
122     { ROT_ARS, ROT_BACKEND_ARS, ROT_FUNCNAMA(ars) },
123     { ROT_AMSAT, ROT_BACKEND_AMSAT, ROT_FUNCNAMA(amsat) },
124     { ROT_TS7400, ROT_BACKEND_TS7400, ROT_FUNCNAMA(ts7400) },
125     { ROT_CELESTRON, ROT_BACKEND_CELESTRON, ROT_FUNCNAMA(celestron) },
126     { ROT_ETHER6, ROT_BACKEND_ETHER6, ROT_FUNCNAMA(ether6) },
127     { ROT_CNCTRK, ROT_BACKEND_CNCTRK, ROT_FUNCNAMA(cnctrk) },
128     { ROT_PROSISTEL, ROT_BACKEND_PROSISTEL, ROT_FUNCNAMA(prosistel) },
129     { ROT_MEADE, ROT_BACKEND_MEADE, ROT_FUNCNAMA(meade) },
130     { ROT_IOPTRON, ROT_BACKEND_IOPTRON, ROT_FUNCNAMA(ioptron) },
131     { ROT_SATEL, ROT_BACKEND_SATEL, ROT_FUNCNAMA(satel) },
132     { ROT_RADANT, ROT_BACKEND_RADANT, ROT_FUNCNAMA(radant)},
133 #if HAVE_LIBINDI
134     { ROT_INDI, ROT_BACKEND_INDI, ROT_FUNCNAMA(indi) },
135 #endif
136     { 0, NULL }, /* end */
137 };
138 
139 
140 // Apparently, no rotator can be probed.
141 
142 /*
143  * ROT_BACKEND_LIST is here, please keep it up to date,
144  *  i.e. each time you implement a new backend.
145  */
146 
147 
148 /*
149  * This struct to keep track of known rot models.
150  * It is chained, and used in a hash table, see below.
151  */
152 //! @cond Doxygen_Suppress
153 struct rot_list
154 {
155     const struct rot_caps *caps;
156     struct rot_list *next;
157 };
158 //! @endcond
159 
160 
161 //! @cond Doxygen_Suppress
162 #define ROTLSTHASHSZ 16
163 #define HASH_FUNC(a) ((a)%ROTLSTHASHSZ)
164 //! @endcond
165 
166 
167 /*
168  * The rot_hash_table is a hash table pointing to a list of next==NULL
169  *  terminated caps.
170  */
171 static struct rot_list *rot_hash_table[ROTLSTHASHSZ] = { NULL, };
172 
173 
174 static int rot_lookup_backend(rot_model_t rot_model);
175 
176 
177 /*
178  * Basically, this is a hash insert function that doesn't check for dup!
179  */
180 //! @cond Doxygen_Suppress
rot_register(const struct rot_caps * caps)181 int HAMLIB_API rot_register(const struct rot_caps *caps)
182 {
183     int hval;
184     struct rot_list *p;
185 
186     if (!caps)
187     {
188         return -RIG_EINVAL;
189     }
190 
191     rot_debug(RIG_DEBUG_VERBOSE, "rot_register (%d)\n", caps->rot_model);
192 
193 #ifndef DONT_WANT_DUP_CHECK
194 
195     if (rot_get_caps(caps->rot_model) != NULL)
196     {
197         return -RIG_EINVAL;
198     }
199 
200 #endif
201 
202     p = (struct rot_list *)malloc(sizeof(struct rot_list));
203 
204     if (!p)
205     {
206         return -RIG_ENOMEM;
207     }
208 
209     hval = HASH_FUNC(caps->rot_model);
210     p->caps = caps;
211     // p->handle = NULL;
212     p->next = rot_hash_table[hval];
213     rot_hash_table[hval] = p;
214 
215     return RIG_OK;
216 }
217 //! @endcond
218 
219 
220 /*
221  * Get rot capabilities.
222  * i.e. rot_hash_table lookup
223  */
224 //! @cond Doxygen_Suppress
rot_get_caps(rot_model_t rot_model)225 const struct rot_caps *HAMLIB_API rot_get_caps(rot_model_t rot_model)
226 {
227     struct rot_list *p;
228 
229     for (p = rot_hash_table[HASH_FUNC(rot_model)]; p; p = p->next)
230     {
231         if (p->caps->rot_model == rot_model)
232         {
233             return p->caps;
234         }
235     }
236 
237     return NULL;    /* sorry, caps not registered! */
238 }
239 //! @endcond
240 
241 
242 /*
243  * lookup for backend index in rot_backend_list table,
244  * according to BACKEND_NUM
245  * return -1 if not found.
246  */
rot_lookup_backend(rot_model_t rot_model)247 static int rot_lookup_backend(rot_model_t rot_model)
248 {
249     int i;
250 
251     for (i = 0; i < ROT_BACKEND_MAX && rot_backend_list[i].be_name; i++)
252     {
253         if (ROT_BACKEND_NUM(rot_model) ==
254                 rot_backend_list[i].be_num)
255         {
256             return i;
257         }
258     }
259 
260     return -1;
261 }
262 
263 
264 /*
265  * rot_check_backend
266  * check the backend declaring this model has been loaded
267  * and if not loaded already, load it!
268  * This permits seamless operation in rot_init.
269  */
270 //! @cond Doxygen_Suppress
rot_check_backend(rot_model_t rot_model)271 int HAMLIB_API rot_check_backend(rot_model_t rot_model)
272 {
273     const struct rot_caps *caps;
274     int be_idx;
275     int retval;
276 
277     /* already loaded ? */
278     caps = rot_get_caps(rot_model);
279 
280     if (caps)
281     {
282         return RIG_OK;
283     }
284 
285     be_idx = rot_lookup_backend(rot_model);
286 
287     /*
288      * Never heard about this backend family!
289      */
290     if (be_idx == -1)
291     {
292         rot_debug(RIG_DEBUG_VERBOSE,
293                   "%s: unsupported backend %d for model %d\n",
294                   __func__,
295                   ROT_BACKEND_NUM(rot_model),
296                   rot_model);
297 
298         return -RIG_ENAVAIL;
299     }
300 
301     retval = rot_load_backend(rot_backend_list[be_idx].be_name);
302 
303     return retval;
304 }
305 //! @endcond
306 
307 
308 //! @cond Doxygen_Suppress
rot_unregister(rot_model_t rot_model)309 int HAMLIB_API rot_unregister(rot_model_t rot_model)
310 {
311     int hval;
312     struct rot_list *p, *q;
313 
314     hval = HASH_FUNC(rot_model);
315     q = NULL;
316 
317     for (p = rot_hash_table[hval]; p; p = p->next)
318     {
319         if (p->caps->rot_model == rot_model)
320         {
321             if (q == NULL)
322             {
323                 rot_hash_table[hval] = p->next;
324             }
325             else
326             {
327                 q->next = p->next;
328             }
329 
330             free(p);
331             return RIG_OK;
332         }
333 
334         q = p;
335     }
336 
337     return -RIG_EINVAL; /* sorry, caps not registered! */
338 }
339 //! @endcond
340 
341 
342 /*
343  * rot_list_foreach
344  * executes cfunc on all the elements stored in the rot hash list
345  */
346 //! @cond Doxygen_Suppress
rot_list_foreach(int (* cfunc)(const struct rot_caps *,rig_ptr_t),rig_ptr_t data)347 int HAMLIB_API rot_list_foreach(int (*cfunc)(const struct rot_caps *,
348                                 rig_ptr_t),
349                                 rig_ptr_t data)
350 {
351     struct rot_list *p;
352     int i;
353 
354     if (!cfunc)
355     {
356         return -RIG_EINVAL;
357     }
358 
359     for (i = 0; i < ROTLSTHASHSZ; i++)
360     {
361         for (p = rot_hash_table[i]; p; p = p->next)
362             if ((*cfunc)(p->caps, data) == 0)
363             {
364                 return RIG_OK;
365             }
366     }
367 
368     return RIG_OK;
369 }
370 //! @endcond
371 
372 
373 /*
374  * rot_probe_all
375  * called straight by rot_probe
376  */
377 //! @cond Doxygen_Suppress
rot_probe_all(hamlib_port_t * p)378 rot_model_t HAMLIB_API rot_probe_all(hamlib_port_t *p)
379 {
380     int i;
381     rot_model_t rot_model;
382 
383     for (i = 0; i < ROT_BACKEND_MAX && rot_backend_list[i].be_name; i++)
384     {
385         if (rot_backend_list[i].be_probe)
386         {
387             rot_model = (*rot_backend_list[i].be_probe)(p);
388 
389             if (rot_model != ROT_MODEL_NONE)
390             {
391                 return rot_model;
392             }
393         }
394     }
395 
396     return ROT_MODEL_NONE;
397 }
398 //! @endcond
399 
400 
401 //! @cond Doxygen_Suppress
rot_load_all_backends()402 int rot_load_all_backends()
403 {
404     int i;
405 
406     for (i = 0; i < ROT_BACKEND_MAX && rot_backend_list[i].be_name; i++)
407     {
408         rot_load_backend(rot_backend_list[i].be_name);
409     }
410 
411     return RIG_OK;
412 }
413 //! @endcond
414 
415 
416 /*
417  * rot_load_backend
418  * Dynamically load a rot backend through dlopen mechanism
419  */
420 //! @cond Doxygen_Suppress
rot_load_backend(const char * be_name)421 int HAMLIB_API rot_load_backend(const char *be_name)
422 {
423     int status;
424     int (*be_init)(rig_ptr_t);
425     int i;
426 
427     for (i = 0; i < ROT_BACKEND_MAX && rot_backend_list[i].be_name; i++)
428     {
429         if (!strcmp(be_name, rot_backend_list[i].be_name))
430         {
431             be_init = rot_backend_list[i].be_init;
432 
433             if (be_init == NULL)
434             {
435                 printf("Null\n");
436                 return -EINVAL;
437             }
438 
439             status = (*be_init)(NULL);
440             return status;
441         }
442     }
443 
444     return -EINVAL;
445 
446 }
447 //! @endcond
448