Skip to content

Commit 2ba0f46

Browse files
committed
treewide: New OPCODES for register read & write
Implement atomic register operations which ensures that a register read or a register write cannot be interrupted by other clients of IIOD. Advantages: - half round trips: single command vs write-address + read-value - binary addresses/values vs hex string converion On client-server compatibility: v1.0 clients can connect to v1.0+ servers (atomic ops used) v1.0 clients connecting to v0.x servers fall back to attribute method Signed-off-by: Dan Nechita <[email protected]>
1 parent 3358f55 commit 2ba0f46

File tree

9 files changed

+235
-7
lines changed

9 files changed

+235
-7
lines changed

device.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -301,13 +301,14 @@ int iio_device_reg_write(struct iio_device *dev,
301301
const struct iio_attr *attr;
302302
const struct iio_backend_ops *ops = dev->ctx->ops;
303303

304-
/* Use atomic backend operation if available (thread-safe for local backend) */
304+
/* Use atomic backend operation if available */
305305
if (ops && ops->reg_write) {
306306
return ops->reg_write(dev, address, value);
307307
}
308308

309-
/* Fallback to the original implementation for other backends.
310-
* NOTE: This has a potential race condition with reg_read operations. */
309+
/* Fallback to the original attribute-based implementation.
310+
* NOTE: This has a potential race condition with reg_read operations
311+
* when multiple clients access the same IIOD server. */
311312
ssize_t ret;
312313
char buf[1024];
313314

@@ -329,13 +330,15 @@ int iio_device_reg_read(struct iio_device *dev,
329330
const struct iio_attr *attr;
330331
const struct iio_backend_ops *ops = dev->ctx->ops;
331332

332-
/* Use atomic backend operation if available (thread-safe for local backend) */
333+
/* Use atomic backend operation if available */
333334
if (ops && ops->reg_read) {
334335
return ops->reg_read(dev, address, value);
335336
}
336337

337-
/* Fallback to the original implementation for other backends.
338-
* NOTE: This has a race condition but it's unlikely to happen in practice. */
338+
/* Fallback to the original attribute-based implementation.
339+
* NOTE: This has a race condition when multiple clients access
340+
* the same IIOD server, as the two-step process (write address,
341+
* then read value) is not atomic. */
339342
long long val;
340343
int ret;
341344

iiod-client.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1885,3 +1885,52 @@ int iiod_client_read_event(struct iio_event_stream_pdata *pdata,
18851885

18861886
return ret;
18871887
}
1888+
1889+
int iiod_client_reg_read(struct iiod_client *client,
1890+
const struct iio_device *dev,
1891+
uint32_t address, uint32_t *value)
1892+
{
1893+
struct iiod_io *io = iiod_responder_get_default_io(client->responder);
1894+
struct iiod_command cmd;
1895+
struct iiod_buf buf;
1896+
int ret;
1897+
1898+
cmd.op = IIOD_OP_REG_READ;
1899+
cmd.dev = (uint8_t) iio_device_get_index(dev);
1900+
cmd.code = (int32_t) address;
1901+
1902+
buf.ptr = value;
1903+
buf.size = sizeof(*value);
1904+
1905+
ret = iiod_io_exec_command(io, &cmd, NULL, &buf);
1906+
if (ret < 0)
1907+
return ret;
1908+
1909+
if (ret != sizeof(*value))
1910+
return -EIO;
1911+
1912+
return 0;
1913+
}
1914+
1915+
int iiod_client_reg_write(struct iiod_client *client,
1916+
const struct iio_device *dev,
1917+
uint32_t address, uint32_t value)
1918+
{
1919+
struct iiod_io *io = iiod_responder_get_default_io(client->responder);
1920+
struct iiod_command cmd;
1921+
struct iiod_buf buf;
1922+
int ret;
1923+
1924+
cmd.op = IIOD_OP_REG_WRITE;
1925+
cmd.dev = (uint8_t) iio_device_get_index(dev);
1926+
cmd.code = (int32_t) address;
1927+
1928+
buf.ptr = &value;
1929+
buf.size = sizeof(value);
1930+
1931+
ret = iiod_io_send_command(io, &cmd, &buf, 1);
1932+
if (ret < 0)
1933+
return ret;
1934+
1935+
return iiod_io_wait_for_command_done(io);
1936+
}

iiod-responder.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ enum iiod_opcode {
5555
IIOD_OP_FREE_EVSTREAM,
5656
IIOD_OP_READ_EVENT,
5757

58+
IIOD_OP_REG_READ,
59+
IIOD_OP_REG_WRITE,
60+
5861
IIOD_NB_OPCODES,
5962
};
6063

iiod/responder.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,6 +1170,70 @@ static void handle_read_event(struct parser_pdata *pdata,
11701170
}
11711171
}
11721172

1173+
static void handle_reg_read(struct parser_pdata *pdata,
1174+
const struct iiod_command *cmd,
1175+
struct iiod_command_data *cmd_data)
1176+
{
1177+
struct iiod_io *io = iiod_command_get_default_io(cmd_data);
1178+
const struct iio_context *ctx = pdata->ctx;
1179+
const struct iio_device *dev;
1180+
uint32_t address, value;
1181+
struct iiod_buf buf;
1182+
int ret;
1183+
1184+
dev = iio_context_get_device(ctx, cmd->dev);
1185+
if (!dev) {
1186+
ret = -ENODEV;
1187+
goto out_send_response;
1188+
}
1189+
1190+
address = (uint32_t) cmd->code;
1191+
1192+
ret = iio_device_reg_read((struct iio_device *)dev, address, &value);
1193+
if (ret < 0) {
1194+
iiod_io_send_response_code(io, ret);
1195+
} else {
1196+
buf.ptr = &value;
1197+
buf.size = sizeof(value);
1198+
iiod_io_send_response(io, sizeof(value), &buf, 1);
1199+
}
1200+
1201+
return;
1202+
1203+
out_send_response:
1204+
iiod_io_send_response_code(io, ret);
1205+
}
1206+
1207+
static void handle_reg_write(struct parser_pdata *pdata,
1208+
const struct iiod_command *cmd,
1209+
struct iiod_command_data *cmd_data)
1210+
{
1211+
struct iiod_io *io = iiod_command_get_default_io(cmd_data);
1212+
const struct iio_context *ctx = pdata->ctx;
1213+
const struct iio_device *dev;
1214+
uint32_t address, value;
1215+
struct iiod_buf buf;
1216+
int ret;
1217+
1218+
dev = iio_context_get_device(ctx, cmd->dev);
1219+
if (!dev) {
1220+
ret = -ENODEV;
1221+
goto out_send_response;
1222+
}
1223+
1224+
address = (uint32_t) cmd->code;
1225+
buf.ptr = &value;
1226+
buf.size = sizeof(value);
1227+
ret = iiod_command_data_read(cmd_data, &buf);
1228+
if (ret < 0)
1229+
goto out_send_response;
1230+
1231+
ret = iio_device_reg_write((struct iio_device *)dev, address, value);
1232+
1233+
out_send_response:
1234+
iiod_io_send_response_code(io, ret);
1235+
}
1236+
11731237
typedef void (*iiod_opcode_fn)(struct parser_pdata *,
11741238
const struct iiod_command *,
11751239
struct iiod_command_data *cmd_data);
@@ -1202,6 +1266,9 @@ static const iiod_opcode_fn iiod_op_functions[] = {
12021266
[IIOD_OP_CREATE_EVSTREAM] = handle_create_evstream,
12031267
[IIOD_OP_FREE_EVSTREAM] = handle_free_evstream,
12041268
[IIOD_OP_READ_EVENT] = handle_read_event,
1269+
1270+
[IIOD_OP_REG_READ] = handle_reg_read,
1271+
[IIOD_OP_REG_WRITE] = handle_reg_write,
12051272
};
12061273

12071274
static int iiod_cmd(const struct iiod_command *cmd,

include/iio/iio-backend.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ struct iio_backend_ops {
139139
struct iio_event *out_event,
140140
bool nonblock);
141141

142-
/* Atomic register operations - optional, for thread-safe local backend */
142+
/* Atomic register operations */
143143
int (*reg_read)(const struct iio_device *dev, uint32_t address, uint32_t *value);
144144
int (*reg_write)(const struct iio_device *dev, uint32_t address, uint32_t value);
145145
};

include/iio/iiod-client.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,13 @@ __api int iiod_client_read_event(struct iio_event_stream_pdata *pdata,
101101
struct iio_event *out_event,
102102
bool nonblock);
103103

104+
__api int iiod_client_reg_read(struct iiod_client *client,
105+
const struct iio_device *dev,
106+
uint32_t address, uint32_t *value);
107+
__api int iiod_client_reg_write(struct iiod_client *client,
108+
const struct iio_device *dev,
109+
uint32_t address, uint32_t value);
110+
104111
#undef __api
105112

106113
#endif /* _IIOD_CLIENT_H */

network.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,36 @@ network_open_events_fd(const struct iio_device *dev)
513513
return iiod_client_open_event_stream(pdata->iiod_client, dev);
514514
}
515515

516+
static int network_reg_read(const struct iio_device *dev,
517+
uint32_t address, uint32_t *value)
518+
{
519+
const struct iio_context *ctx = iio_device_get_context(dev);
520+
struct iio_context_pdata *pdata = iio_context_get_pdata(ctx);
521+
struct iiod_client *client = pdata->iiod_client;
522+
523+
if (!iiod_client_uses_binary_interface(client)) {
524+
/* Fallback to attribute-based method for ASCII protocol */
525+
return -ENOSYS;
526+
}
527+
528+
return iiod_client_reg_read(client, dev, address, value);
529+
}
530+
531+
static int network_reg_write(const struct iio_device *dev,
532+
uint32_t address, uint32_t value)
533+
{
534+
const struct iio_context *ctx = iio_device_get_context(dev);
535+
struct iio_context_pdata *pdata = iio_context_get_pdata(ctx);
536+
struct iiod_client *client = pdata->iiod_client;
537+
538+
if (!iiod_client_uses_binary_interface(client)) {
539+
/* Fallback to attribute-based method for ASCII protocol */
540+
return -ENOSYS;
541+
}
542+
543+
return iiod_client_reg_write(client, dev, address, value);
544+
}
545+
516546
static const struct iio_backend_ops network_ops = {
517547
.scan = IF_ENABLED(HAVE_DNS_SD, dnssd_context_scan),
518548
.create = network_create_context,
@@ -539,6 +569,9 @@ static const struct iio_backend_ops network_ops = {
539569
.open_ev = network_open_events_fd,
540570
.close_ev = iiod_client_close_event_stream,
541571
.read_ev = iiod_client_read_event,
572+
573+
.reg_read = network_reg_read,
574+
.reg_write = network_reg_write,
542575
};
543576

544577
__api_export_if(WITH_NETWORK_BACKEND_DYNAMIC)

serial.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,36 @@ serial_open_events_fd(const struct iio_device *dev)
291291
return iiod_client_open_event_stream(pdata->iiod_client, dev);
292292
}
293293

294+
static int serial_reg_read(const struct iio_device *dev,
295+
uint32_t address, uint32_t *value)
296+
{
297+
const struct iio_context *ctx = iio_device_get_context(dev);
298+
struct iio_context_pdata *pdata = iio_context_get_pdata(ctx);
299+
struct iiod_client *client = pdata->iiod_client;
300+
301+
if (!iiod_client_uses_binary_interface(client)) {
302+
/* Fallback to attribute-based method for ASCII protocol */
303+
return -ENOSYS;
304+
}
305+
306+
return iiod_client_reg_read(client, dev, address, value);
307+
}
308+
309+
static int serial_reg_write(const struct iio_device *dev,
310+
uint32_t address, uint32_t value)
311+
{
312+
const struct iio_context *ctx = iio_device_get_context(dev);
313+
struct iio_context_pdata *pdata = iio_context_get_pdata(ctx);
314+
struct iiod_client *client = pdata->iiod_client;
315+
316+
if (!iiod_client_uses_binary_interface(client)) {
317+
/* Fallback to attribute-based method for ASCII protocol */
318+
return -ENOSYS;
319+
}
320+
321+
return iiod_client_reg_write(client, dev, address, value);
322+
}
323+
294324
static const struct iio_backend_ops serial_ops = {
295325
.create = serial_create_context_from_args,
296326
.read_attr = serial_read_attr,
@@ -314,6 +344,9 @@ static const struct iio_backend_ops serial_ops = {
314344
.open_ev = serial_open_events_fd,
315345
.close_ev = iiod_client_close_event_stream,
316346
.read_ev = iiod_client_read_event,
347+
348+
.reg_read = serial_reg_read,
349+
.reg_write = serial_reg_write,
317350
};
318351

319352
__api_export_if(WITH_SERIAL_BACKEND_DYNAMIC)

usb.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,36 @@ static int usb_set_timeout(struct iio_context *ctx, unsigned int timeout)
275275
return iiod_client_set_timeout(pdata->io_ctx.iiod_client, timeout);
276276
}
277277

278+
static int usb_reg_read(const struct iio_device *dev,
279+
uint32_t address, uint32_t *value)
280+
{
281+
const struct iio_context *ctx = iio_device_get_context(dev);
282+
struct iio_context_pdata *pdata = iio_context_get_pdata(ctx);
283+
struct iiod_client *client = pdata->io_ctx.iiod_client;
284+
285+
if (!iiod_client_uses_binary_interface(client)) {
286+
/* Fallback to attribute-based method for ASCII protocol */
287+
return -ENOSYS;
288+
}
289+
290+
return iiod_client_reg_read(client, dev, address, value);
291+
}
292+
293+
static int usb_reg_write(const struct iio_device *dev,
294+
uint32_t address, uint32_t value)
295+
{
296+
const struct iio_context *ctx = iio_device_get_context(dev);
297+
struct iio_context_pdata *pdata = iio_context_get_pdata(ctx);
298+
struct iiod_client *client = pdata->io_ctx.iiod_client;
299+
300+
if (!iiod_client_uses_binary_interface(client)) {
301+
/* Fallback to attribute-based method for ASCII protocol */
302+
return -ENOSYS;
303+
}
304+
305+
return iiod_client_reg_write(client, dev, address, value);
306+
}
307+
278308
static const struct iio_device * usb_get_trigger(const struct iio_device *dev)
279309
{
280310
const struct iio_context *ctx = iio_device_get_context(dev);
@@ -538,6 +568,9 @@ static const struct iio_backend_ops usb_ops = {
538568
.open_ev = usb_open_events_fd,
539569
.close_ev = iiod_client_close_event_stream,
540570
.read_ev = iiod_client_read_event,
571+
572+
.reg_read = usb_reg_read,
573+
.reg_write = usb_reg_write,
541574
};
542575

543576
__api_export_if(WITH_USB_BACKEND_DYNAMIC)

0 commit comments

Comments
 (0)