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