1 /* $NetBSD: icom.c,v 1.2 2003/12/04 16:23:36 drochner Exp $ */
4 * Program to control ICOM radios
6 * This is a ripoff of the utility routines in the ICOM software
7 * distribution. The only function provided is to load the radio
8 * frequency. All other parameters must be manually set before use.
22 #define BMAX 50 /* max command length */
23 #define DICOM /dev/icom/ /* ICOM port link */
28 #define S_IDLE 0 /* idle */
29 #define S_HDR 1 /* header */
30 #define S_TX 2 /* address */
31 #define S_DATA 3 /* data */
32 #define S_ERROR 4 /* error */
35 * Local function prototypes
37 static void doublefreq
P((double, u_char
*, int));
38 static int sndpkt
P((int, int, u_char
*, u_char
*));
39 static int sndoctet
P((int, int));
40 static int rcvoctet
P((int));
45 static int flags
; /* trace flags */
46 static int state
; /* fsa state */
50 * icom_freq(fd, ident, freq) - load radio frequency
53 icom_freq( /* returns 0 (ok), EIO (error) */
54 int fd
, /* file descriptor */
55 int ident
, /* ICOM radio identifier */
56 double freq
/* frequency (MHz) */
59 u_char cmd
[BMAX
], rsp
[BMAX
];
66 doublefreq(freq
* 1e6
, &cmd
[1], temp
);
67 temp
= sndpkt(fd
, ident
, cmd
, rsp
);
68 if (temp
< 1 || rsp
[0] != ACK
)
75 * doublefreq(freq, y, len) - double to ICOM frequency with padding
78 doublefreq( /* returns void */
79 double freq
, /* frequency */
80 u_char
*x
, /* radio frequency */
81 int len
/* length (octets) */
88 sprintf(s1
, " %10.0f", freq
);
93 x
[i
] = x
[i
] | ((*y
-- & 0x0f) << 4);
105 * These routines send a packet and receive the response. If an error
106 * (collision) occurs on transmit, the packet is resent. If an error
107 * occurs on receive (timeout), all input to the terminating FI is
108 * discarded and the packet is resent. If the maximum number of retries
109 * is not exceeded, the program returns the number of octets in the user
110 * buffer; otherwise, it returns zero.
114 * Frames begin with a two-octet preamble PR-PR followyd by the
115 * transceiver address RE, controller address TX, control code CN, zero
116 * or more data octets DA (depending on command), and terminator FI.
117 * Since the bus is bidirectional, every octet output is echoed on
118 * input. Every valid frame sent is answered with a frame in the same
119 * format, but with the RE and TX fields interchanged. The CN field is
120 * set to NAK if an error has occurred. Otherwise, the data are returned
121 * in this and following DA octets. If no data are returned, the CN
122 * octet is set to ACK.
124 * +------+------+------+------+------+--//--+------+
125 * | PR | PR | RE | TX | CN | DA | FI |
126 * +------+------+------+------+------+--//--+------+
129 * icom_open() - open and initialize serial interface
131 * This routine opens the serial interface for raw transmission; that
132 * is, character-at-a-time, no stripping, checking or monkeying with the
133 * bits. For Unix, an input operation ends either with the receipt of a
134 * character or a 0.5-s timeout.
138 char *device
, /* device name/link */
139 int speed
, /* line speed */
140 int trace
/* trace flags */ )
146 fd
= open(device
, O_RDWR
, 0777);
150 tcgetattr(fd
, &ttyb
);
151 ttyb
.c_iflag
= 0; /* input modes */
152 ttyb
.c_oflag
= 0; /* output modes */
153 ttyb
.c_cflag
= IBAUD
|CS8
|CREAD
|CLOCAL
; /* control modes */
154 ttyb
.c_lflag
= 0; /* local modes */
155 ttyb
.c_cc
[VMIN
] = 0; /* min chars */
156 ttyb
.c_cc
[VTIME
] = 5; /* receive timeout */
157 cfsetispeed(&ttyb
, (u_int
)speed
);
158 cfsetospeed(&ttyb
, (u_int
)speed
);
159 tcsetattr(fd
, TCSANOW
, &ttyb
);
165 * sndpkt(r, x, y) - send packet and receive response
167 * This routine sends a command frame, which consists of all except the
168 * preamble octets PR-PR. It then listens for the response frame and
169 * returns the payload to the caller. The routine checks for correct
170 * response header format; that is, the length of the response vector
171 * returned to the caller must be at least 2 and the RE and TX octets
172 * must be interchanged; otherwise, the operation is retried up to
173 * the number of times specified in a global variable.
175 * The trace function, which is enabled by the P_TRACE bit of the global
176 * flags variable, prints all characters received or echoed on the bus
177 * preceded by a T (transmit) or R (receive). The P_ERMSG bit of the
178 * flags variable enables printing of bus error messages.
180 * Note that the first octet sent is a PAD in order to allow time for
181 * the radio to flush its receive buffer after sending the previous
182 * response. Even with this precaution, some of the older radios
183 * occasionally fail to receive a command and it has to be sent again.
186 sndpkt( /* returns octet count */
187 int fd
, /* file descriptor */
188 int r
, /* radio address */
189 u_char
*cmd
, /* command vector */
190 u_char
*rsp
/* response vector */
195 (void)tcflush(fd
, TCIOFLUSH
);
196 for (i
= 0; i
< RETRY
; i
++) {
204 sndoctet(fd
, PAD
); /* send header */
209 for (j
= 0; j
< BMAX
; j
++) { /* send body */
210 if (sndoctet(fd
, cmd
[j
]) == FI
)
213 while (rcvoctet(fd
) != FI
); /* purge echos */
214 if (cmd
[0] == V_FREQT
|| cmd
[0] == V_MODET
)
215 return (0); /* shortcut for broadcast */
218 * Receive packet. First, delete all characters
219 * preceeding a PR, then discard all PRs. Check that the
220 * RE and TX fields are correctly interchanged, then
221 * copy the remaining data and FI to the user buffer.
226 while ((temp
= rcvoctet(fd
)) != FI
) {
238 } else if (temp
!= TX
) {
261 "icom: buffer overrun\n");
265 rsp
[j
++] = (u_char
)temp
;
280 printf("icom: retries exceeded\n");
288 * These routines read and write octets on the bus. In case of receive
289 * timeout a FI code is returned. In case of output collision (echo
290 * does not match octet sent), the remainder of the collision frame
291 * (including the trailing FI) is discarded.
294 * sndoctet(fd, x) - send octet
297 sndoctet( /* returns octet */
298 int fd
, /* file descriptor */
311 * rcvoctet(fd) - receive octet
314 rcvoctet( /* returns octet */
315 int fd
/* file descriptor */
320 if (read(fd
, &y
, 1) < 1)
321 y
= FI
; /* come here if timeout */
322 if (flags
& P_TRACE
&& y
!= PAD
)