-
Notifications
You must be signed in to change notification settings - Fork 0
/
SoapyRPC.hpp
316 lines (307 loc) · 9.25 KB
/
SoapyRPC.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
// SoapyRPC.hpp - remote procedure call endpoint
// Copyright (c) 2021 Phil Ashby
// SPDX-License-Identifier: BSL-1.0
#ifndef SoapyRPC_hpp
#define SoapyRPC_hpp
// This RPC implementation uses text I/O over TCP
// in the tradition of many 'simple xxx' internet
// protocols.
#include <stdio.h>
#include <cstring>
#include <SoapySDR/Logger.hpp>
// map of format names to frame sizes
const std::map<std::string, size_t> g_frameSizes = {
{ "CS8", 2 }, { "CS16", 4 }, { "CF32", 8 },
};
// RPC separator
const std::string TCPREMOTE_RPC_SEP = "--";
// RPC codes
enum
{
// internal specials - socket type identifier, single digit value please..
TCPREMOTE_RPC_LOAD,
TCPREMOTE_LOG_STREAM,
TCPREMOTE_DATA_SEND,
TCPREMOTE_DATA_RECV,
// identification API
TCPREMOTE_GET_HARDWARE_KEY = 10,
TCPREMOTE_GET_HARDWARE_INFO,
// channel API
TCPREMOTE_SET_FRONTEND_MAPPING,
TCPREMOTE_GET_FRONTEND_MAPPING,
TCPREMOTE_GET_NUM_CHANNELS,
TCPREMOTE_GET_CHANNEL_INFO,
TCPREMOTE_GET_FULL_DUPLEX,
// stream API
TCPREMOTE_GET_STREAM_FORMATS,
TCPREMOTE_GET_STREAM_NATIVE_FORMAT,
TCPREMOTE_GET_STREAM_ARGS_INFO,
TCPREMOTE_SETUP_STREAM,
TCPREMOTE_CLOSE_STREAM,
TCPREMOTE_GET_STREAM_MTU,
TCPREMOTE_ACTIVATE_STREAM,
TCPREMOTE_DEACTIVATE_STREAM,
// direct buffer access API - bit tricky over a network, no RPCs =)
// antenna API
TCPREMOTE_LIST_ANTENNAS,
TCPREMOTE_SET_ANTENNA,
TCPREMOTE_GET_ANTENNA,
// frontend corrections API
TCPREMOTE_HAS_DC_OFFSET_MODE,
TCPREMOTE_SET_DC_OFFSET_MODE,
TCPREMOTE_GET_DC_OFFSET_MODE,
TCPREMOTE_HAS_DC_OFFSET,
TCPREMOTE_SET_DC_OFFSET,
TCPREMOTE_GET_DC_OFFSET,
TCPREMOTE_HAS_IQ_BALANCE,
TCPREMOTE_SET_IQ_BALANCE,
TCPREMOTE_GET_IQ_BALANCE,
TCPREMOTE_HAS_FREQUENCY_CORRECTION,
TCPREMOTE_SET_FREQUENCY_CORRECTION,
TCPREMOTE_GET_FREQUENCY_CORRECTION,
// gain API
TCPREMOTE_LIST_GAINS,
TCPREMOTE_HAS_GAIN_MODE,
TCPREMOTE_SET_GAIN_MODE,
TCPREMOTE_GET_GAIN_MODE,
TCPREMOTE_SET_GAIN,
TCPREMOTE_SET_GAIN_NAMED,
TCPREMOTE_GET_GAIN,
TCPREMOTE_GET_GAIN_NAMED,
TCPREMOTE_GET_GAIN_RANGE,
TCPREMOTE_GET_GAIN_RANGE_NAMED,
// frequency API
TCPREMOTE_SET_FREQUENCY,
TCPREMOTE_SET_FREQUENCY_NAMED,
TCPREMOTE_GET_FREQUENCY,
TCPREMOTE_GET_FREQUENCY_NAMED,
TCPREMOTE_LIST_FREQUENCIES,
TCPREMOTE_GET_FREQUENCY_RANGE,
TCPREMOTE_GET_FREQUENCY_RANGE_NAMED,
TCPREMOTE_GET_FREQUENCY_ARGS_INFO,
// sample rate API
TCPREMOTE_SET_SAMPLE_RATE,
TCPREMOTE_GET_SAMPLE_RATE,
// list rates deprecated, we emulate in client side
TCPREMOTE_GET_SAMPLE_RATE_RANGE,
// bandwidth API
TCPREMOTE_SET_BANDWIDTH,
TCPREMOTE_GET_BANDWIDTH,
// list bandwidths deprecated, we emulate in client side
TCPREMOTE_GET_BANDWIDTH_RANGE,
// clocking API
TCPREMOTE_SET_MASTER_CLOCK_RATE,
TCPREMOTE_GET_MASTER_CLOCK_RATE,
TCPREMOTE_GET_MASTER_CLOCK_RATES,
TCPREMOTE_LIST_CLOCK_SOURCES,
TCPREMOTE_SET_CLOCK_SOURCE,
TCPREMOTE_GET_CLOCK_SOURCE,
// time API
TCPREMOTE_LIST_TIME_SOURCES,
TCPREMOTE_SET_TIME_SOURCE,
TCPREMOTE_GET_TIME_SOURCE,
TCPREMOTE_HAS_HARDWARE_TIME,
TCPREMOTE_GET_HARDWARE_TIME,
TCPREMOTE_SET_HARDWARE_TIME,
TCPREMOTE_SET_COMMAND_TIME,
// sensor API
TCPREMOTE_LIST_SENSORS,
TCPREMOTE_GET_SENSOR_INFO,
TCPREMOTE_READ_SENSOR,
TCPREMOTE_LIST_CHANNEL_SENSORS,
TCPREMOTE_GET_CHANNEL_SENSOR_INFO,
TCPREMOTE_READ_CHANNEL_SENSOR,
// register API
TCPREMOTE_LIST_REGISTER_INTERFACES,
TCPREMOTE_WRITE_REGISTER_NAMED,
TCPREMOTE_READ_REGISTER_NAMED,
TCPREMOTE_WRITE_REGISTER,
TCPREMOTE_READ_REGISTER,
TCPREMOTE_WRITE_REGISTERS_NAMED,
TCPREMOTE_READ_REGISTERS_NAMED,
// settings API
TCPREMOTE_GET_SETTING_INFO,
TCPREMOTE_WRITE_SETTING,
TCPREMOTE_READ_SETTING,
TCPREMOTE_GET_CHANNEL_SETTING_INFO,
TCPREMOTE_WRITE_CHANNEL_SETTING,
TCPREMOTE_READ_CHANNEL_SETTING,
// GPIO API
TCPREMOTE_LIST_GPIO_BANKS,
TCPREMOTE_WRITE_GPIO,
TCPREMOTE_WRITE_GPIO_MASKED,
TCPREMOTE_READ_GPIO,
TCPREMOTE_WRITE_GPIO_DIR,
TCPREMOTE_READ_GPIO_DIR,
// I2C API
TCPREMOTE_WRITE_I2C,
TCPREMOTE_READ_I2C,
// SPI API
TCPREMOTE_TRANSACT_SPI,
// UART API
TCPREMOTE_LIST_UARTS,
TCPREMOTE_WRITE_UART,
TCPREMOTE_READ_UART,
// internal special - dropping connection
TCPREMOTE_DROP_RPC = 1000
};
// Design notes:
// - holds error state, to ensure no further I/O is attempted once errored,
// this allows strerror()/perror() to work despite subsequent rpc methods
// and allows error to be detected at the end of a series of rpc methods.
class SoapyRPC
{
public:
SoapyRPC(int socket) {
hasError = false;
handle = fdopen(socket, "r+");
if (handle) {
setlinebuf(handle);
SoapySDR_logf(SOAPY_SDR_TRACE, "SoapyRPC::<cons>(%d)", socket);
} else {
SoapySDR_logf(SOAPY_SDR_ERROR, "SoapyRPC::<cons>, failed to fdopen socket: %s", strerror(errno));
hasError = true;
}
}
SoapyRPC(FILE *fp) {
hasError = false;
handle = fp;
setlinebuf(handle);
SoapySDR_logf(SOAPY_SDR_TRACE, "SoapyRPC::<cons>(%p=>%d)", fp, fileno(fp));
}
~SoapyRPC() {
SoapySDR_logf(SOAPY_SDR_TRACE, "SoapyRPC::<dest>(%d)", handle? fileno(handle): -1);
if (handle)
fclose(handle);
}
int writeInteger(const int i) {
if (hasError) return -1;
SoapySDR_logf(SOAPY_SDR_TRACE, "Wi %d", i);
int r = fprintf(handle, "%d\n", i);
if (r<0) {
SoapySDR_logf(SOAPY_SDR_ERROR, "SoapyRPC::writeInteger: %s", strerror(errno));
hasError = true;
}
return r;
}
int writeDouble(const double d) {
if (hasError) return -1;
SoapySDR_logf(SOAPY_SDR_TRACE, "Wd %f", d);
int r = fprintf(handle, "%f\n", d);
if (r<0) {
SoapySDR_logf(SOAPY_SDR_ERROR, "SoapyRPC::writeDouble %s", strerror(errno));
hasError = true;
}
return r;
}
int writeString(const std::string s) {
if (hasError) return -1;
SoapySDR_logf(SOAPY_SDR_TRACE, "Ws %s", s.c_str());
int r = fprintf(handle, "%s\n", s.c_str());
if (r<0) {
SoapySDR_logf(SOAPY_SDR_ERROR, "SoapyRPC::writeString: %s", strerror(errno));
hasError = true;
}
return r;
}
int writeKwargs(const SoapySDR::Kwargs &args) {
if (hasError) return -1;
int n = 0, r;
for (auto it=args.begin(); it!=args.end(); ++it) {
std::string nv;
nv += it->first;
nv += '=';
nv += it->second;
r = writeString(nv);
if (r<0)
return r;
n += r;
}
r = writeString("="); // terminator
if (r<0)
return r;
n += r;
return n;
}
int writeStrVector(const std::vector<std::string> &vec) {
if (hasError) return -1;
int n = 0, r;
for (auto it: vec) {
r = writeString(it);
if (r<0)
return r;
n += r;
}
r = writeString("");
if (r<0)
return r;
n += r;
return n;
}
int readInteger() {
if (hasError) return -1;
int rv=-1;
std::string s = readString();
if (s.length()>0)
sscanf(s.c_str(), "%d", &rv);
else
SoapySDR_log(SOAPY_SDR_ERROR, "SoapyRPC::readInteger, empty line");
return rv;
}
double readDouble() {
if (hasError) return -1;
double rv=NAN;
std::string s = readString();
if (s.length()>0)
sscanf(s.c_str(), "%lf", &rv);
else
SoapySDR_log(SOAPY_SDR_ERROR, "SoapyRPC::readDouble, empty line");
return rv;
}
std::string readString() {
char line[256];
std::string rv;
if (hasError) return rv;
if (fgets(line, sizeof(line), handle)) {
line[strlen(line)-1] = 0;
rv = line;
} else {
SoapySDR_logf(SOAPY_SDR_ERROR, "SoapyRPC::readString: %s", strerror(errno));
hasError = true;
}
SoapySDR_logf(SOAPY_SDR_TRACE, "R '%s'", rv.c_str());
return rv;
}
SoapySDR::Kwargs readKwargs() {
SoapySDR::Kwargs args;
if (hasError) return args;
while (true) {
std::string nv = readString();
if (nv.length()<2) // '=' or empty is a terminator
break;
size_t p = nv.find('=');
if (p>0) {
args[nv.substr(0,p)] = nv.substr(p+1);
} else {
SoapySDR_logf(SOAPY_SDR_INFO,"SoapyRPC::readKwargs, ignored invalid line: %s", nv.c_str());
}
}
return args;
}
std::vector<std::string> readStrVector() {
std::vector<std::string> list;
if (hasError) return list;
while (true) {
// blank/error indicates end of list
std::string str = readString();
if (str.length()==0)
break;
list.push_back(str);
}
return list;
}
private:
FILE *handle;
bool hasError;
};
#endif