1 /*
2  *  Hamlib PRM80 backend - main file
3  *  Copyright (c) 2010,2021 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 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>  /* String function definitions */
29 #include <unistd.h>  /* UNIX standard function definitions */
30 #include <ctype.h>
31 #include <math.h>
32 
33 #include "hamlib/rig.h"
34 #include "serial.h"
35 #include "cal.h"
36 #include "register.h"
37 #include "idx_builtin.h"
38 
39 #include "prm80.h"
40 
41 
42 #define LF "\x0a"
43 
44 #define BUFSZ 64
45 
46 // Channel number min and max
47 #define CHAN_MIN 0
48 #define CHAN_MAX 99
49 
50 // "E" system state is cached for this time (ms)
51 #define PRM80_CACHE_TIMEOUT 200
52 
53 // Length (in bytes) of the response to the "E" command
54 #define CMD_E_RSP_LEN 22
55 
56 #define RX_IF_OFFSET MHz(21.4)
57 
58 // The rig's PLL only deals with freq in Hz divided by this value
59 #define FREQ_DIV 12500.
60 
61 /* V5 based on V4 commands
62  * retrieved from https://github.com/f4fez/prm80
63  * and https://github.com/f4fez/prm80/blob/master/doc/Computer_commands_V4.md
64  * It used to be from https://sourceforge.net/projects/prm80/
65  * and https://sourceforge.net/p/prm80/wiki/Computer%20commands%20V4/
66 
67 MessageVersion:
68 IF TARGET EQ 8060
69               DB   "PRM8060 V4.0"
70 ELSEIF TARGET EQ 8070
71               DB   "PRM8070 V4.0"
72 ENDIF
73 
74 MessageAide:  DB   "H",0Dh,0Ah
75               DB   " Commandes disponibles :",0Dh,0Ah
76               DB   " [0] = Reset.",0Dh,0Ah
77               DB   " [1] a [5] = Show 80c552 port state P1 to P5.",0Dh,0Ah
78               DB   " [C] = Print channels list.",0Dh,0Ah
79               DB   " [D] = Set system byte.",0Dh,0Ah
80               DB   " [E] = Show system state (Mode-Chan-Chanstate-Sql-Vol-Lock-RX freq-TX freq,RSSI).",0Dh,0Ah
81               DB   " [F] = Set squelch.",0Dh,0Ah
82               DB   " [H] = Print this help page.",0Dh,0Ah
83               DB   " [I] = Erase and init RAM and EEPROM.",0Dh,0Ah
84               DB   " [K] = Set lock byte.",0Dh,0Ah
85               DB   " [L] = Print latch state.",0Dh,0Ah
86               DB   " [M] = Edit external RAM manualy.",0Dh,0Ah
87               DB   " [N] = Set current channel.",0Dh,0Ah
88               DB   " [O] = Set volume.",0Dh,0Ah
89               DB   " [P] = Edit/Add channel.",0Dh,0Ah
90               DB   " [Q] = Set channels number.",0Dh,0Ah
91               DB   " [R] = Set synthetiser frequencies.",0Dh,0Ah
92               DB   " [U] = Print 80c552 internal RAM.",0Dh,0Ah
93               DB   " [S] = Copy EEPROM to external RAM.",0Dh,0Ah
94               DB   " [T] = Set current channel state.",0Dh,0Ah
95               DB   " [V] = Print firmware version.",0Dh,0Ah
96               DB   " [X] = Copy external RAM to EEPROM.",0Dh,0Ah
97               DB   " [Y] = Print first 2 kb from the EEPROM I2C 24c16.",0Dh,0Ah
98               DB   " [Z] = Print external RAM ($0000 to $07FF).",0Dh,0Ah,0
99 */
100 /*
101 [0] = Reset.
102 [C] = Print channels list.
103 [D] = Set system byte.
104 
105 [E] = Show system state (Mode-Chan-Chanstate-Sql-Vol-Lock-RX freq-TX
106                 freq-RSSI).
107 [F] = Set squelch.
108 [H] = Print this help page.
109 [K] = Set lock byte.
110 [N] = Set current channel.
111 [O] = Set volume.
112 [P] = Edit/Add channel.
113 [Q] = Set channels number.
114 [R] = Set synthetiser frequencies.
115 [T] = Set current channel state.
116 [V] = Print firmware version.
117 */
118 
119 /*
120  * Mode byte, which holds the state of system basic features:
121     b0: Squelch mode is displayed on LCD if true. Channel mode if false.
122     b1: Power level (High or Low mode)
123     b2: Squelch open (Read only)
124     b3: TX mode (Read only)
125     b4: PLL locked (Read only)
126     b5: Long key push (Internal)
127     b6: Key bounce (Internal)
128     b7: Force LCD refresh when set. Automaticaly cleared.
129 
130    Channel state byte:
131     b0: Shift enable when true
132     b1: Reverse mode when true
133     b2: Positive shift when true. Negative if false
134     b3: Scanning locked out channel if set
135     b4-7: na.
136 
137    Lock byte, which disables user controls when connected to a computer
138     b0: Keys disabled when true
139     b1: TX disabled when true
140     b2: Volume button disabled when true
141     b3: RX disabled when true
142     b4-b7: na.
143 
144  * *********************************************************************
145  */
146 
prm80_force_cache_timeout(RIG * rig)147 static void prm80_force_cache_timeout(RIG *rig)
148 {
149     struct prm80_priv_data *priv = (struct prm80_priv_data *)rig->state.priv;
150 
151     rig_force_cache_timeout(&priv->status_tv);
152 }
153 
154 /*
155  * Read a prompt terminated by delimiter, then write an optional string s.
156  */
read_prompt_and_send(hamlib_port_t * rigport,char * data,int * data_len,const char * s,const char * delimiter,int space_after_delim)157 static int read_prompt_and_send(hamlib_port_t *rigport,
158                                 char *data, int *data_len, const char *s, const char *delimiter,
159                                 int space_after_delim)
160 {
161     char buf[BUFSZ];
162     char spacebuf[4];
163     int buflen, retval;
164 
165     /* no data wanted? flush it anyway by reading it */
166     if (data == NULL)
167     {
168         data = buf;
169     }
170 
171     buflen = (data_len == NULL) ? sizeof(buf) : *data_len;
172 
173     retval = read_string(rigport, data, buflen, delimiter, 1);
174 
175     if (retval < 0)
176     {
177         return retval;
178     }
179 
180     // Place an end of string
181     data[(retval < buflen) ? retval : (buflen - 1)] = '\0';
182 
183     if (data_len != NULL)
184     {
185         *data_len = retval;
186     }
187 
188     // Read one (dummy) space character after the colon
189     if (space_after_delim)
190     {
191         retval = read_block(rigport, spacebuf, 1);
192 
193         if (retval < 0 && retval != -RIG_ETIMEOUT)
194         {
195             return retval;
196         }
197     }
198 
199     // Here is the answer to the prompt
200     retval = write_block(rigport, s, strlen(s));
201 
202     return retval;
203 }
204 
205 /*
206  * Read a prompt terminated by ": ", then write an optional string s.
207  */
read_colon_prompt_and_send(hamlib_port_t * rigport,char * data,int * data_len,const char * s)208 static int read_colon_prompt_and_send(hamlib_port_t *rigport,
209                                       char *data, int *data_len, const char *s)
210 {
211     return read_prompt_and_send(rigport, data, data_len, s, ":", 1);
212 }
213 
214 /*
215  * Read a prompt terminated by "$" (without space afterwards),
216  * then write an optional string s.
217  */
read_dollar_prompt_and_send(hamlib_port_t * rigport,char * data,int * data_len,const char * s)218 static int read_dollar_prompt_and_send(hamlib_port_t *rigport,
219                                        char *data, int *data_len, const char *s)
220 {
221     return read_prompt_and_send(rigport, data, data_len, s, "$", 0);
222 }
223 
224 /*
225  * After each executed command, the rig generally sends "\r\n>"
226  */
prm80_wait_for_prompt(hamlib_port_t * rigport)227 static int prm80_wait_for_prompt(hamlib_port_t *rigport)
228 {
229     char buf[BUFSZ * 2];
230     int retval;
231 
232     // Read up to the '>' prompt and discard content.
233     retval = read_string(rigport, buf, sizeof(buf), ">", 1);
234 
235     if (retval < 0)
236     {
237         return retval;
238     }
239 
240     return RIG_OK;
241 }
242 
243 /*
244  *
245  * \param cmd is string of generally one letter (or digit)
246  * \param arg1 is an optional string to send afterwards
247  * \param wait_prompt boolean when non-nul, will wait for "\r\n>" afterwards
248  */
prm80_transaction(RIG * rig,const char * cmd,const char * arg1,int wait_prompt)249 static int prm80_transaction(RIG *rig, const char *cmd,
250                              const char *arg1, int wait_prompt)
251 {
252     int retval;
253     struct rig_state *rs = &rig->state;
254 
255     // Get rid of possible prompt sent by the rig
256     rig_flush(&rs->rigport);
257 
258     // Start with the command
259     retval = write_block(&rs->rigport, cmd, strlen(cmd));
260 
261     if (retval != RIG_OK)
262     {
263         return retval;
264     }
265 
266     if (arg1 != NULL)
267     {
268         retval = read_colon_prompt_and_send(&rs->rigport, NULL, NULL, arg1);
269 
270         if (retval < 0)
271         {
272             return retval;
273         }
274     }
275 
276     if (wait_prompt)
277     {
278         prm80_wait_for_prompt(&rs->rigport);
279     }
280 
281     return RIG_OK;
282 }
283 
prm80_init(RIG * rig)284 int prm80_init(RIG *rig)
285 {
286     if (!rig)
287     {
288         return -RIG_EINVAL;
289     }
290 
291     rig->state.priv = (void *)calloc(1, sizeof(struct prm80_priv_data));
292 
293     if (!rig->state.priv)
294     {
295         /* whoops! memory shortage! */
296         return -RIG_ENOMEM;
297     }
298 
299     return RIG_OK;
300 }
301 
prm80_cleanup(RIG * rig)302 int prm80_cleanup(RIG *rig)
303 {
304     if (rig == NULL)
305     {
306         return -RIG_EINVAL;
307     }
308 
309     free(rig->state.priv);
310     rig->state.priv = NULL;
311 
312     return RIG_OK;
313 }
314 
315 
316 /*
317  * prm80_reset
318  * Assumes rig!=NULL
319  */
prm80_reset(RIG * rig,reset_t reset)320 int prm80_reset(RIG *rig, reset_t reset)
321 {
322     int retval;
323 
324     /*
325      * Reset CPU
326      */
327     retval = prm80_transaction(rig, "0", NULL, 1);
328 
329     if (retval != RIG_OK)
330     {
331         return retval;
332     }
333 
334     prm80_force_cache_timeout(rig);
335 
336     return RIG_OK;
337 }
338 
339 /*
340  * Convert freq in Hz to the RX PLL value representation with PRM08 firmware
341  */
rx_freq_to_pll_value(freq_t rx_freq)342 static unsigned rx_freq_to_pll_value(freq_t rx_freq)
343 {
344     // UHF vs VHF
345     if (rx_freq > MHz(300))
346     {
347         return (unsigned)((rx_freq - RX_IF_OFFSET) / FREQ_DIV);
348     }
349     else
350     {
351         return (unsigned)((rx_freq + RX_IF_OFFSET) / FREQ_DIV);
352     }
353 }
354 
pll_value_to_rx_freq(unsigned pll_value)355 static freq_t pll_value_to_rx_freq(unsigned pll_value)
356 {
357     freq_t rx_freq;
358 
359     rx_freq = (freq_t)pll_value * FREQ_DIV;
360 
361     // UHF vs VHF
362     if (rx_freq > MHz(300))
363     {
364         rx_freq += RX_IF_OFFSET;
365     }
366     else
367     {
368         rx_freq -= RX_IF_OFFSET;
369     }
370 
371     return rx_freq;
372 }
373 
374 /*
375  * Set RX and TX freq
376  *
377  * See https://github.com/f4fez/prm80/blob/master/doc/Computer_control.md
378  *   "Adding a new channel" regarding freq format.
379  */
prm80_set_rx_tx_freq(RIG * rig,freq_t rx_freq,freq_t tx_freq)380 int prm80_set_rx_tx_freq(RIG *rig, freq_t rx_freq, freq_t tx_freq)
381 {
382     struct rig_state *rs = &rig->state;
383     char rx_freq_buf[BUFSZ];
384     char tx_freq_buf[BUFSZ];
385     int rc;
386 
387     // for RX, compute the PLL word without the IF
388     sprintf(rx_freq_buf, "%04X",
389             rx_freq_to_pll_value(rx_freq));
390     sprintf(tx_freq_buf, "%04X",
391             (unsigned)(tx_freq / FREQ_DIV));
392 
393     // The protocol is like this :
394     // "RX frequency : " XXXX
395     // CRLF"TX frequency : " XXXX
396 
397     rc = prm80_transaction(rig, "R", rx_freq_buf, 0);
398 
399     if (rc != RIG_OK)
400     {
401         return rc;
402     }
403 
404     // There's a second line to process after prm80_transaction()
405     rc = read_colon_prompt_and_send(&rs->rigport, NULL, NULL, tx_freq_buf);
406 
407     if (rc != RIG_OK)
408     {
409         return rc;
410     }
411 
412     // quid timeout in trx waiting for freq ?
413 
414     // NB: the [R] command does not update the checksum of the RAM!
415 
416     prm80_wait_for_prompt(&rs->rigport);
417 
418     return rc;
419 }
420 
421 /*
422  * Set (RX) freq
423  */
prm80_set_freq(RIG * rig,vfo_t vfo,freq_t freq)424 int prm80_set_freq(RIG *rig, vfo_t vfo, freq_t freq)
425 {
426     struct prm80_priv_data *priv = (struct prm80_priv_data *)rig->state.priv;
427     freq_t tx_freq;
428     int rc;
429 
430     if (priv->split == RIG_SPLIT_OFF)
431     {
432         tx_freq = freq;
433     }
434     else
435     {
436         tx_freq = (priv->tx_freq == 0.) ? freq : priv->tx_freq;
437     }
438 
439     rc = prm80_set_rx_tx_freq(rig, freq, tx_freq);
440 
441     if (rc == RIG_OK)
442     {
443         priv->rx_freq = freq;
444     }
445 
446     prm80_force_cache_timeout(rig);
447 
448     return rc;
449 }
450 
451 /*
452  * Set TX freq depending on emulated split state
453  */
prm80_set_split_freq(RIG * rig,vfo_t vfo,freq_t tx_freq)454 int prm80_set_split_freq(RIG *rig, vfo_t vfo, freq_t tx_freq)
455 {
456     struct prm80_priv_data *priv = (struct prm80_priv_data *)rig->state.priv;
457     freq_t rx_freq;
458     int rc;
459 
460     rx_freq = (priv->rx_freq == 0.) ? tx_freq : priv->rx_freq;
461 
462     rc = prm80_set_rx_tx_freq(rig, rx_freq, tx_freq);
463 
464     if (rc == RIG_OK)
465     {
466         priv->tx_freq = tx_freq;
467     }
468 
469     prm80_force_cache_timeout(rig);
470 
471     return rc;
472 }
473 
474 /*
475  * Get RX freq depending on emulated split state
476  */
prm80_get_freq(RIG * rig,vfo_t vfo,freq_t * freq)477 int prm80_get_freq(RIG *rig, vfo_t vfo, freq_t *freq)
478 {
479     struct prm80_priv_data *priv = (struct prm80_priv_data *)rig->state.priv;
480     int ret;
481     channel_t chan;
482 
483     memset(&chan, 0, sizeof(chan));
484     chan.vfo = RIG_VFO_CURR;
485 
486     ret = prm80_get_channel(rig, vfo, &chan, 0);
487 
488     if (ret != RIG_OK)
489     {
490         return ret;
491     }
492 
493     *freq = chan.freq;
494     priv->tx_freq = chan.tx_freq;
495 
496     return RIG_OK;
497 }
498 
499 /*
500  * Enable/disable Split
501  *
502  * Rem: don't care about vfo
503  */
prm80_set_split_vfo(RIG * rig,vfo_t vfo,split_t split,vfo_t tx_vfo)504 int prm80_set_split_vfo(RIG *rig, vfo_t vfo, split_t split, vfo_t tx_vfo)
505 {
506     struct prm80_priv_data *priv = (struct prm80_priv_data *)rig->state.priv;
507 
508     priv->split = split;
509 
510     return RIG_OK;
511 }
512 
513 /*
514  * Get Split
515  */
prm80_get_split_vfo(RIG * rig,vfo_t vfo,split_t * split,vfo_t * tx_vfo)516 int prm80_get_split_vfo(RIG *rig, vfo_t vfo, split_t *split, vfo_t *tx_vfo)
517 {
518     struct prm80_priv_data *priv = (struct prm80_priv_data *)rig->state.priv;
519 
520     *split = priv->split;
521     *tx_vfo = RIG_VFO_CURR;
522 
523     return RIG_OK;
524 }
525 
526 /*
527  * Get TX freq
528  */
prm80_get_split_freq(RIG * rig,vfo_t vfo,freq_t * tx_freq)529 int prm80_get_split_freq(RIG *rig, vfo_t vfo, freq_t *tx_freq)
530 {
531     struct prm80_priv_data *priv = (struct prm80_priv_data *)rig->state.priv;
532     int ret;
533     channel_t chan;
534 
535     memset(&chan, 0, sizeof(chan));
536     chan.vfo = RIG_VFO_CURR;
537 
538     ret = prm80_get_channel(rig, vfo, &chan, 0);
539 
540     if (ret != RIG_OK)
541     {
542         return ret;
543     }
544 
545     *tx_freq = chan.tx_freq;
546     priv->rx_freq = chan.freq;
547 
548     return RIG_OK;
549 }
550 
551 /*
552  * Basic helper to ease some generic applications
553  */
prm80_get_mode(RIG * rig,vfo_t vfo,rmode_t * mode,pbwidth_t * width)554 int prm80_get_mode(RIG *rig, vfo_t vfo, rmode_t *mode, pbwidth_t *width)
555 {
556     // Can only do FM
557     *mode = RIG_MODE_FM;
558     *width = rig_passband_normal(rig, *mode);
559 
560     return RIG_OK;
561 }
562 
563 
564 /*
565  * prm80_set_mem
566  * Assumes rig!=NULL
567  */
prm80_set_mem(RIG * rig,vfo_t vfo,int ch)568 int prm80_set_mem(RIG *rig, vfo_t vfo, int ch)
569 {
570     char chbuf[BUFSZ];
571 
572     /* [N] = Set current channel. */
573 
574     if (ch < CHAN_MIN || ch > CHAN_MAX)
575     {
576         return -RIG_EINVAL;
577     }
578 
579     sprintf(chbuf, "%02u", (unsigned)ch);
580 
581     prm80_force_cache_timeout(rig);
582 
583     // Send command, no answer expected from rig except ">" prompt
584 
585     return prm80_transaction(rig, "N", chbuf, 1);
586 }
587 
588 /*
589  * prm80_get_mem
590  * Assumes rig!=NULL
591  */
prm80_get_mem(RIG * rig,vfo_t vfo,int * ch)592 int prm80_get_mem(RIG *rig, vfo_t vfo, int *ch)
593 {
594     int ret;
595     channel_t chan;
596 
597     memset(&chan, 0, sizeof(chan));
598     chan.vfo = RIG_VFO_CURR;
599 
600     ret = prm80_get_channel(rig, vfo, &chan, 0);
601 
602     if (ret != RIG_OK)
603     {
604         return ret;
605     }
606 
607     *ch = chan.channel_num;
608 
609     return RIG_OK;
610 }
611 
612 /*
613  * Convert first two hexadecimal digit to integer
614  */
hhtoi(const char * p)615 static unsigned hhtoi(const char *p)
616 {
617     char buf[4];
618 
619     // it has to be hex digits
620     if (!isxdigit(p[0]) || !isxdigit(p[1]))
621     {
622         rig_debug(RIG_DEBUG_ERR, "%s: unexpected content '%s'\n", __func__, p);
623         return 0;
624     }
625 
626     buf[0] = p[0];
627     buf[1] = p[1];
628     buf[2] = '\0';
629 
630     return (unsigned)strtol(buf, NULL, 16);
631 }
632 
633 /**
634  * Get system state [E] from rig into \a statebuf
635  */
prm80_do_read_system_state(hamlib_port_t * rigport,char * statebuf)636 static int prm80_do_read_system_state(hamlib_port_t *rigport, char *statebuf)
637 {
638     char *p;
639     int ret;
640 
641     // Get rid of possible prompt sent by the rig
642     rig_flush(rigport);
643 
644     /* [E] = Show system state */
645     ret = write_block(rigport, "E", 1);
646 
647     if (ret < 0)
648     {
649         RETURNFUNC(ret);
650     }
651 
652     // The response length is fixed
653     ret = read_block(rigport, statebuf, CMD_E_RSP_LEN);
654 
655     if (ret < 0)
656     {
657         return ret;
658     }
659 
660     if (ret >= 0)
661     {
662         statebuf[ret] = '\0';
663     }
664 
665     if (ret < CMD_E_RSP_LEN)
666     {
667         rig_debug(RIG_DEBUG_ERR, "%s: len=%d < %d, statebuf='%s'\n", __func__,
668                   ret, CMD_E_RSP_LEN, statebuf);
669         RETURNFUNC(-RIG_EPROTO);
670     }
671 
672     p = strchr(statebuf, '>');
673 
674     if (p)
675     {
676         int left_to_read = (p - statebuf) + 1;
677         memmove(statebuf, p + 1, CMD_E_RSP_LEN - left_to_read);
678         ret = read_block(rigport, statebuf + CMD_E_RSP_LEN - left_to_read,
679                          left_to_read);
680 
681         if (ret < 0)
682         {
683             return ret;
684         }
685         else
686         {
687             statebuf[CMD_E_RSP_LEN] = '\0';
688         }
689 
690         rig_debug(RIG_DEBUG_WARN, "%s: len=%d, statebuf='%s'\n", __func__, ret,
691                   statebuf);
692     }
693 
694     prm80_wait_for_prompt(rigport);
695 
696     return RIG_OK;
697 }
698 
699 /*
700  * Layer to handle the cache to Get system state [E]
701  */
prm80_read_system_state(RIG * rig,char * statebuf)702 static int prm80_read_system_state(RIG *rig, char *statebuf)
703 {
704     struct prm80_priv_data *priv = (struct prm80_priv_data *)rig->state.priv;
705     int ret = RIG_OK;
706 
707     if (rig_check_cache_timeout(&priv->status_tv, PRM80_CACHE_TIMEOUT))
708     {
709         ret = prm80_do_read_system_state(&rig->state.rigport, statebuf);
710 
711         if (ret == RIG_OK)
712         {
713             strcpy(priv->cached_statebuf, statebuf);
714 
715             /* update cache date */
716             gettimeofday(&priv->status_tv, NULL);
717         }
718     }
719     else
720     {
721         strcpy(statebuf, priv->cached_statebuf);
722     }
723 
724     return ret;
725 }
726 
727 /*
728  * prm80_get_channel
729  * Assumes rig!=NULL
730  */
prm80_get_channel(RIG * rig,vfo_t vfo,channel_t * chan,int read_only)731 int prm80_get_channel(RIG *rig, vfo_t vfo, channel_t *chan, int read_only)
732 {
733     struct prm80_priv_data *priv = (struct prm80_priv_data *)rig->state.priv;
734     char statebuf[BUFSZ];
735     int ret, chanstate, mode_byte, lock_byte;
736 
737     if (chan->vfo == RIG_VFO_MEM)
738     {
739         ret = prm80_set_mem(rig, RIG_VFO_CURR, chan->channel_num);
740 
741         if (ret != RIG_OK)
742         {
743             return ret;
744         }
745     }
746 
747     ret = prm80_read_system_state(rig, statebuf);
748 
749     if (ret != RIG_OK)
750     {
751         return ret;
752     }
753 
754     /* (Mode-Chan-Chanstate-Sql-Vol-Lock-RX freq-TX freq-RSSI). */
755     /* Examples: 1240080AFF0033F02D40__ or 14000C00FD0079708020__ */
756 
757     /* Current mode:
758        ; b0: Squelch       b1: power
759        ; b2: Squelch open  b3: TX
760        ; b4: PLL locked    b5: Long press memorize
761        ; b6: Debouncing in effect b7: LCD refresh
762       */
763     mode_byte = hhtoi(statebuf);
764 
765     chan->mode = RIG_MODE_FM;
766     chan->width = rig_passband_normal(rig, chan->mode);
767     chan->channel_num = hhtoi(statebuf + 2);
768     chan->tx_mode = chan->mode;
769     chan->tx_width = chan->width;
770 
771     /* Chan state:
772        ; b0: shift enabled   b1: reverse
773        ; b2: shift +         b3: lock out
774      */
775     chanstate = hhtoi(statebuf + 4) & 0x0f;
776     /* is it rptr_shift or split mode ? */
777     chan->rptr_shift = (chanstate & 0x01) == 0 ? RIG_RPT_SHIFT_NONE :
778                        (chanstate & 0x02) ? RIG_RPT_SHIFT_MINUS :
779                        (chanstate & 0x04) ? RIG_RPT_SHIFT_PLUS : RIG_RPT_SHIFT_NONE;
780     chan->flags = (chanstate & 0x08) ? RIG_CHFLAG_SKIP : 0;
781 
782     // squelch is in low nibble
783     chan->levels[LVL_SQL].f = ((float)(hhtoi(statebuf + 6) & 0x0F)) / 15.;
784     // volume is hex "00" .. "10"
785     chan->levels[LVL_AF].f  = ((float)hhtoi(statebuf + 8)) / 16.;
786     chan->levels[LVL_RFPOWER].f  = (mode_byte & 0x02) ? 1.0 : 0.0;
787 
788     // new in FW V5
789     chan->levels[LVL_RAWSTR].i = hhtoi(statebuf + 20);
790 
791     chan->funcs  = 0;
792     chan->funcs |= (chanstate & 0x02) ? RIG_FUNC_REV : 0;
793 
794     lock_byte = hhtoi(statebuf + 10) & 0x0f;
795     chan->funcs |= (lock_byte & 0x05) ? RIG_FUNC_LOCK : 0;
796     chan->funcs |= (lock_byte & 0x08) ? RIG_FUNC_MUTE : 0;
797 
798     chan->freq = pll_value_to_rx_freq((hhtoi(statebuf + 12) << 8) + hhtoi(
799                                           statebuf + 14));
800     chan->tx_freq = ((hhtoi(statebuf + 16) << 8) + hhtoi(statebuf + 18)) * FREQ_DIV;
801 
802     if (chan->rptr_shift != RIG_RPT_SHIFT_NONE)
803     {
804         chan->rptr_offs = chan->tx_freq - chan->freq;
805         chan->split = RIG_SPLIT_OFF;
806     }
807     else
808     {
809         chan->rptr_offs = 0;
810         chan->split = priv->split; // RIG_SPLIT_ON; ?
811     }
812 
813     if (!read_only)
814     {
815         // Set rig to channel values
816         rig_debug(RIG_DEBUG_WARN,
817                   "%s: please contact hamlib mailing list to implement this\n", __func__);
818         rig_debug(RIG_DEBUG_WARN,
819                   "%s: need to know if rig updates when channel read or not\n", __func__);
820         //return -RIG_ENIMPL;
821     }
822 
823     return RIG_OK;
824 }
825 
826 /*
827  * prm80_set_channel handles RIG_VFO_MEM and RIG_VFO_CURR
828  */
prm80_set_channel(RIG * rig,vfo_t vfo,const channel_t * chan)829 int prm80_set_channel(RIG *rig, vfo_t vfo, const channel_t *chan)
830 {
831     struct prm80_priv_data *priv = (struct prm80_priv_data *)rig->state.priv;
832     struct rig_state *rs = &rig->state;
833     char buf[BUFSZ];
834     int ret, chanstate;
835     freq_t tx_freq;
836 
837     if (chan->vfo == RIG_VFO_MEM)
838     {
839         // setting channel without calling set_mem()
840 
841         if (chan->channel_num < CHAN_MIN || chan->channel_num > CHAN_MAX)
842         {
843             return -RIG_EINVAL;
844         }
845 
846         /* [P] = Edit/Add channel */
847         /* Example
848            Channel to set : 00
849            PLL value to load : $8020
850            Channel state : $00
851 
852           Possibly:
853             "This channel number doesn't exist. Add new channel (Y/N) ? "
854            */
855 
856         sprintf(buf, "%02u", (unsigned)chan->channel_num);
857 
858         ret = prm80_transaction(rig, "P", buf, 0);
859 
860         if (ret != RIG_OK)
861         {
862             return ret;
863         }
864 
865         // Set the RX frequency as PLL word.
866         sprintf(buf, "%04X", rx_freq_to_pll_value(chan->freq));
867 
868         // "PLL value to load : $"
869         ret = read_dollar_prompt_and_send(&rs->rigport, NULL, NULL, buf);
870 
871         if (ret != RIG_OK)
872         {
873             return ret;
874         }
875 
876         // the channel status byte.
877         switch (chan->rptr_shift)
878         {
879         case RIG_RPT_SHIFT_NONE : chanstate = 0x00; break;
880 
881         case RIG_RPT_SHIFT_MINUS : chanstate = 0x03; break;
882 
883         case RIG_RPT_SHIFT_PLUS : chanstate = 0x05; break;
884 
885         default: chanstate = 0x00; break;
886         }
887 
888         chanstate |= (chan->flags & RIG_CHFLAG_SKIP) ? 0x08 : 0;
889 
890         sprintf(buf, "%02X", chanstate);
891 
892         // "Channel state : $"
893         ret = read_dollar_prompt_and_send(&rs->rigport, NULL, NULL, buf);
894 
895         if (ret != RIG_OK)
896         {
897             return ret;
898         }
899 
900         // Determine if prompt came back (CRLF'>') or have to
901         // handle the possible query from the rig:
902         // "This channel number doesn't exist. Add new channel (Y/N) ? "
903         ret = read_block(&rs->rigport, buf, 3);
904 
905         if (ret < 0)
906         {
907             RETURNFUNC(ret);
908         }
909 
910         if (ret == 3 && buf[2] == 'T')
911         {
912             // Read the question
913             ret = read_string(&rs->rigport, buf, sizeof(buf), "?", 1);
914 
915             if (ret < 0)
916             {
917                 RETURNFUNC(ret);
918             }
919 
920             // Read extra space
921             ret = read_block(&rs->rigport, buf, 1);
922 
923             if (ret < 0)
924             {
925                 RETURNFUNC(ret);
926             }
927 
928             // Send confirmation
929             ret = write_block(&rs->rigport, "Y", 1);
930 
931             if (ret < 0)
932             {
933                 RETURNFUNC(ret);
934             }
935         }
936 
937         prm80_wait_for_prompt(&rs->rigport);
938     }
939     else
940     {
941         // assume here chan->vfo == RIG_VFO_CURR
942         // that is the "RAM" VFO not backed by memory
943 
944         tx_freq = (chan->split == RIG_SPLIT_ON) ? chan->tx_freq : chan->freq;
945 
946         ret = prm80_set_rx_tx_freq(rig, chan->freq, tx_freq);
947 
948         if (ret != RIG_OK)
949         {
950             return ret;
951         }
952 
953         priv->split = chan->split;
954         priv->rx_freq = chan->freq;
955         priv->tx_freq = tx_freq;
956 
957         ret = prm80_set_level(rig, vfo, RIG_LEVEL_SQL, chan->levels[LVL_SQL]);
958 
959         if (ret != RIG_OK)
960         {
961             return ret;
962         }
963 
964         ret = prm80_set_level(rig, vfo, RIG_LEVEL_AF, chan->levels[LVL_AF]);
965 
966         if (ret != RIG_OK)
967         {
968             return ret;
969         }
970 
971 #if 0
972         // Not implemented yet..
973         ret = prm80_set_level(rig, vfo, RIG_LEVEL_RFPOWER, chan->levels[LVL_RFPOWER]);
974 
975         if (ret != RIG_OK)
976         {
977             return ret;
978         }
979 
980 #endif
981 
982         ret = prm80_set_func(rig, vfo, RIG_FUNC_LOCK,
983                              !!(chan->funcs & RIG_FUNC_LOCK));
984 
985         if (ret != RIG_OK)
986         {
987             return ret;
988         }
989     }
990 
991     prm80_force_cache_timeout(rig);
992 
993     return RIG_OK;
994 }
995 
996 
997 // TODO FUNC_REV through Channel state byte ?
998 // TODO "Read-Modify-Write" (or shadowing in priv area) of the lock bits
prm80_set_func(RIG * rig,vfo_t vfo,setting_t func,int status)999 int prm80_set_func(RIG *rig, vfo_t vfo, setting_t func, int status)
1000 {
1001     int ret;
1002 
1003     if (func & RIG_FUNC_LOCK)
1004     {
1005         /* Lock keys(b0)/Vol(b2) */
1006         ret = prm80_transaction(rig, "K", (status != 0) ? "05" : "00", 1);
1007     }
1008     else if (func & RIG_FUNC_MUTE)
1009     {
1010         /* Lock RX(b3) */
1011         ret = prm80_transaction(rig, "K", (status != 0) ? "08" : "00", 1);
1012     }
1013     else
1014     {
1015         ret = -RIG_EINVAL;
1016     }
1017 
1018     prm80_force_cache_timeout(rig);
1019 
1020     return ret;
1021 }
1022 
prm80_get_func(RIG * rig,vfo_t vfo,setting_t func,int * status)1023 int prm80_get_func(RIG *rig, vfo_t vfo, setting_t func, int *status)
1024 {
1025     int ret;
1026     channel_t chan;
1027 
1028     memset(&chan, 0, sizeof(chan));
1029     chan.vfo = RIG_VFO_CURR;
1030 
1031     ret = prm80_get_channel(rig, vfo, &chan, 0);
1032 
1033     if (ret != RIG_OK)
1034     {
1035         return ret;
1036     }
1037 
1038     *status = !!(chan.funcs & func);
1039 
1040     return RIG_OK;
1041 }
1042 
1043 /*
1044  * prm80_set_level
1045  * Assumes rig!=NULL
1046  */
prm80_set_level(RIG * rig,vfo_t vfo,setting_t level,value_t val)1047 int prm80_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val)
1048 {
1049     char buf[BUFSZ];
1050     int ret, mode_byte;
1051 
1052     // do some clamping, all levels are float values.
1053     if (val.f < 0.0)
1054     {
1055         val.f = 0.0;
1056     }
1057     else if (val.f > 1.0)
1058     {
1059         val.f = 1.0;
1060     }
1061 
1062     switch (level)
1063     {
1064     case RIG_LEVEL_AF:
1065         // Unlike system state, volume decimal
1066         sprintf(buf, "%02u", (unsigned)(val.f * 16));
1067 
1068         return prm80_transaction(rig, "O", buf, 1);
1069 
1070     case RIG_LEVEL_SQL:
1071         sprintf(buf, "%02u", (unsigned)(val.f * 15));
1072 
1073         return prm80_transaction(rig, "F", buf, 1);
1074 
1075     case RIG_LEVEL_RFPOWER:
1076         /* Current mode:
1077            ; b0: Squelch       b1: power
1078            ; b2: Squelch open  b3: TX
1079            ; b4: PLL locked    b5: Long press memorize
1080            ; b6: Debouncing in effect b7: LCD refresh
1081            */
1082         // Perform a "Read-Modify-Write" of the mode_byte
1083         ret = prm80_read_system_state(rig, buf);
1084 
1085         if (ret != RIG_OK)
1086         {
1087             return ret;
1088         }
1089 
1090         mode_byte  = hhtoi(buf);
1091         mode_byte &= ~0x02;
1092         mode_byte |= (val.f == 0.) ? 0 : 0x02;
1093         sprintf(buf, "%02X", (unsigned)mode_byte);
1094 
1095         return prm80_transaction(rig, "D", buf, 1);
1096 
1097     default:
1098         rig_debug(RIG_DEBUG_ERR, "%s: unsupported set_level %s\n", __func__,
1099                   rig_strlevel(level));
1100         return -RIG_EINVAL;
1101     }
1102 
1103     prm80_force_cache_timeout(rig);
1104 
1105     return RIG_OK;
1106 }
1107 
1108 #ifdef V4_ONLY
1109 /*
1110  * get_level RIG_LEVEL_RAWSTR
1111  */
prm80_get_rawstr_RAM(RIG * rig,value_t * val)1112 static int prm80_get_rawstr_RAM(RIG *rig, value_t *val)
1113 {
1114     char buf[BUFSZ];
1115     struct rig_state *rs = &rig->state;
1116     int ret, i;
1117 
1118     /* [U] = Print 80c552 internal RAM. */
1119 
1120     // Send cmd, Wait for colon prompt, but then send nothing
1121     ret = prm80_transaction(rig, "U", "", 0);
1122 
1123     if (ret < 0)
1124     {
1125         return ret;
1126     }
1127 
1128     // Read CRLF
1129     ret = read_string(&rs->rigport, buf, BUFSZ, "\n", 1);
1130 
1131     if (ret < 0)
1132     {
1133         return ret;
1134     }
1135 
1136     // (16 lines of 16 bytes each)
1137 
1138     // According to prm.a51, the rssi_hold variable is in RAM at RAM+35.
1139     // The RAM base is at 030h.
1140 
1141 #define RSSI_HOLD_ADDR (0x30 + 35)  // = 0x53
1142 
1143     for (i = 0; i < (RSSI_HOLD_ADDR / 16) + 1; i++)
1144     {
1145         ret = read_string(&rs->rigport, buf, BUFSZ, "\n", 1);
1146 
1147         if (ret < 0)
1148         {
1149             return ret;
1150         }
1151     }
1152 
1153     // A line looks like this
1154     // "$50 : 00 01 02 53 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\r\n"
1155 
1156     val->i = hhtoi(buf + 6 + 3 * (RSSI_HOLD_ADDR % 16));
1157 
1158     // discard the remaining content of RAM print
1159     for (i = 0; i < (16 - RSSI_HOLD_ADDR / 16) - 1; i++)
1160     {
1161         read_string(&rs->rigport, buf, BUFSZ, "\n", 1);
1162     }
1163 
1164     prm80_wait_for_prompt(&rs->rigport);
1165 
1166     return RIG_OK;
1167 }
1168 #endif
1169 
1170 /*
1171  * prm80_get_level
1172  * Assumes rig!=NULL
1173  */
prm80_get_level(RIG * rig,vfo_t vfo,setting_t level,value_t * val)1174 int prm80_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val)
1175 {
1176     int ret;
1177     channel_t chan;
1178 
1179     memset(&chan, 0, sizeof(chan));
1180     chan.vfo = RIG_VFO_CURR;
1181 
1182     ret = prm80_get_channel(rig, vfo, &chan, 0);
1183 
1184     if (ret != RIG_OK)
1185     {
1186         return ret;
1187     }
1188 
1189     switch (level)
1190     {
1191     case RIG_LEVEL_RAWSTR:
1192         val->i = chan.levels[LVL_RAWSTR].i;
1193 
1194         break;
1195 
1196     case RIG_LEVEL_AF:
1197         val->f = chan.levels[LVL_AF].f;
1198 
1199         break;
1200 
1201     case RIG_LEVEL_SQL:
1202         val->f = chan.levels[LVL_SQL].f;
1203 
1204         break;
1205 
1206     case RIG_LEVEL_RFPOWER:
1207         val->f = chan.levels[LVL_RFPOWER].f;
1208 
1209         break;
1210 
1211     default:
1212         rig_debug(RIG_DEBUG_ERR, "%s: unsupported set_level %s\n", __func__,
1213                   rig_strlevel(level));
1214         return -RIG_EINVAL;
1215     }
1216 
1217     return RIG_OK;
1218 }
1219 
prm80_get_ptt(RIG * rig,vfo_t vfo,ptt_t * ptt)1220 int prm80_get_ptt(RIG *rig, vfo_t vfo, ptt_t *ptt)
1221 {
1222     char statebuf[BUFSZ];
1223     int ret, mode_byte;
1224 
1225     ret = prm80_read_system_state(rig, statebuf);
1226 
1227     if (ret != RIG_OK)
1228     {
1229         return ret;
1230     }
1231 
1232     mode_byte = hhtoi(statebuf);
1233 
1234     // TX mode on?
1235     *ptt = (mode_byte & 0x08) ? RIG_PTT_ON : RIG_PTT_OFF;
1236 
1237     return RIG_OK;
1238 }
1239 
prm80_get_dcd(RIG * rig,vfo_t vfo,dcd_t * dcd)1240 int prm80_get_dcd(RIG *rig, vfo_t vfo, dcd_t *dcd)
1241 {
1242     char statebuf[BUFSZ];
1243     int ret, mode_byte;
1244 
1245     ret = prm80_read_system_state(rig, statebuf);
1246 
1247     if (ret != RIG_OK)
1248     {
1249         return ret;
1250     }
1251 
1252     mode_byte = hhtoi(statebuf);
1253 
1254     // Squelch open?
1255     *dcd = (mode_byte & 0x04) ? RIG_DCD_ON : RIG_DCD_OFF;
1256 
1257     return RIG_OK;
1258 }
1259 
1260 // TODO vfo_op : MCL FROM_VFO ..
1261 
1262 /*
1263  * prm80_get_info
1264  * Assumes rig!=NULL
1265  */
prm80_get_info(RIG * rig)1266 const char *prm80_get_info(RIG *rig)
1267 {
1268     static char s_buf[BUFSZ];
1269     struct rig_state *rs = &rig->state;
1270     char *p;
1271     int ret;
1272 
1273     // Get rid of possible prompt sent by the rig
1274     rig_flush(&rs->rigport);
1275 
1276     /* [V] = Print firmware version. */
1277     ret = write_block(&rs->rigport, "V", 1);
1278 
1279     if (ret < 0)
1280     {
1281         return NULL;
1282     }
1283 
1284     ret = read_string(&rs->rigport, s_buf, BUFSZ, ">", 1);
1285 
1286     if (ret < 0)
1287     {
1288         return NULL;
1289     }
1290 
1291     p = strchr(s_buf, '\r');
1292 
1293     if (p)
1294     {
1295         // chomp
1296         *p = '\0';
1297     }
1298 
1299     return s_buf;
1300 }
1301 
1302 
1303 /*
1304  * initrigs_prm80 is called by rig_backend_load
1305  */
DECLARE_INITRIG_BACKEND(prm80)1306 DECLARE_INITRIG_BACKEND(prm80)
1307 {
1308     rig_debug(RIG_DEBUG_VERBOSE, "%s: _init called\n", __func__);
1309 
1310     rig_register(&prm8060_caps);
1311 
1312     return RIG_OK;
1313 }
1314 
1315