This repository has been archived by the owner on Mar 5, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
/
oni-spec-wip.txt
714 lines (572 loc) · 28.8 KB
/
oni-spec-wip.txt
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
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
---
title: |
Open Neuro Interface Specification \
Version 0.3
author: Jonathan P. Newman, Wilson Lab, MIT
institution:
date: \today{}
geometry: margin=2cm
header-includes:
- \usepackage{setspace}
- \usepackage{lineno}
- \linenumbers
colorlinks: true
toc: true
toc-depth: 2
secnumdepth: 2
abstract: |
This document specifies requirements for implementing an Open Neuro
Interface (ONI) acquisition system in hardware and software. This
specification entails two basic elements: (1) Communication protocols
between acquisition firmware and host software and (2) an application
programming interface (API) for utilizing this communication protocol. This
document is incomplete and we gratefully welcome criticisms and amendments.
---
\newpage
# Intentions and capabilities
- Potential for low latency round trip times (sub millisecond)
- Potential for high-bandwidth, bidirectional communication (> 1000 neural data
channels)
- Acquisition and control of arbitrary of hardware components using a single
communication medium
- Support generic mixes of hardware elements from multiple, asynchronous
pieces of hardware
- Generic hardware configuration
- Generic data input streams
- Generic data output streams
- Support multiple acquisition systems on one computer
- Cross platform
- Aimed at the creation of firmware, APIs, language bindings and
application-specific libraries
\newpage
# FPGA/Host PC communication {#comm-protocol}
Communication between the acquisition board firmware and API shall occur
over at least four communication channels:
1. Signal: Read-only, short-message, asynchronous hardware events. Only one
signal channel is permitted.
2. Configuration: Bidirectional, register-based, synchronous configuration
setting and getting. Only one configuration channel is permitted.
3. Input: Read-only, asynchronous, high-bandwidth firmware to host streaming.
More than one input channel is permitted.
4. Output: Write-only, asynchronous, high-bandwidth host to firmware streaming.
More than one output channel is permitted.
Required characteristics of these channels are described in the following
paragraphs.
## Signal channel {#sig-chan}
- __Word size__ : 8 bits
- __R/W Timing__ : Asynchronous
- __Read Only__
The _signal_ channel provides a way for the FPGA firmware to inform host of
configuration results, which may be provided with a significant delay.
Additionally, it allows the host to read the device map supported by the FPGA
firmware. The behavior of the signal channel is equivalent to a read-only,
blocking UNIX named pipe. Signal data is framed into packets using Consistent
Overhead Byte Stuffing
([COBS](https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing)).
Within this scheme, packets are delimited using 0's and always have the
following format:
```
... | PACKET_FLAG data | ...
```
where `PACKET_FLAG` is 32-bit unsigned integer with a single unique bit
setting, ` | ` represents a packet delimiter, and `...` represents other
packets. This stream can be read and ignored until a desired packet is
received. Reading this stream shall block if no data is available, which allows
asynchronous configuration acknowledgment. Valid `PACKET_FLAG`s are:
``` {.c}
enum signal {
NULLSIG = (1u << 0), // Null signal, ignored by host
CONFIGWACK = (1u << 1), // Configuration write-acknowledgment
CONFIGWNACK = (1u << 2), // Configuration no-write-acknowledgment
CONFIGRACK = (1u << 3), // Configuration read-acknowledgment
CONFIGRNACK = (1u << 4), // Configuration no-read-acknowledgment
DEVICEMAPACK = (1u << 5), // Device map start acknowledgment
DEVICEINST = (1u << 6), // Device map instance
};
```
Following a hardware reset, the signal channel is used to provide the device
map to the host using the following packet sequence:
```
... | DEVICEMAPACK, uint32_t num_devices | DEVICEINST device dev_0, uint32_t dev_0_port
| DEVICEINST device dev_1, uint32_t dev_1_port | ... | DEVICEINST device dev_n, uint32_t dev_n_port| ...
```
Where dev_n is the numerical ID of each device and dev_n_port the physical port to which the hub containing this device is connected.
Following a device register read or write (see [configuration
channel](#conf-chan)), ACK or NACK signals are pushed onto the signal stream by
the firmware. For instance, on a successful register read:
```
... | CONFIGRACK, uint32_t register value | ...
```
## Configuration channel {#conf-chan}
- __Word size__ : 32 bits
- __R/W Timing__ : Synchronous
- __Read & Write__
The _configuration_ channel supports seeking to, reading, and writing a set of
configuration registers. Its behavior is equivalent to that of a normal UNIX
file. There are two classes of registers handled by the configuration channel:
the first set of registers encapsulates a generic device register programming
interface. The remaining registers are for global context control and
configuration and provide access to acquisition parameters and state control.
`SEEK` locations of each configuration register, relative to the start of the
stream, should be hard-coded into the API implementation file and used in the
background to manipulate register state.
### Device register programming interface
The device programming interface is composed of the following configuration
channel registers:
- `uint32_t config_device_idx`: Device index register. Specify a device
endpoint as enumerated in the device map by the firmware (e.g. an Intan chip,
or a IMU chip) and to which communication will be directed using
`config_reg_addr` and `config_reg_value`, as described below.
- `uint32_t config_reg_addr`: The register address of configuration to be
written
- `uint32_t config_reg_value`: configuration value to be written to or read
from and that corresponds to `config_reg_addr` on device `config_device_id`
- `uint32_t config_rw`: A flag indicating if a read or write should be
performed. 0 indicates read operation. A value > 0 indicates write
operation.
- `uint32_t config_trig`: Set > 0 to trigger either register read or write
operation depending on the state of `config_rw`. If `config_rw` is 0, a read
is performed. In this case `config_reg_value` is updated with value stored at
`config_reg_addr` on device at `config_device_id`. If `config_rw` is 1,
`config_reg_value` is written to register at `config_reg_addr` on device
`config_device_id`. The `config_trig` register is always be set low by the
firmware following transmission even if it is not successful or does not make
sense given the address register values.
Appropriate values of `config_reg_addr` and `config_reg_value` are
determined by:
- Looking at a device's data sheet if the device is an integrated circuit
- Examining the ONI-compliant API's documentation which contains off register
addresses and descriptions for devices officially supported by this project.
When a host requests a device register _read_, the following following actions
take place:
1. The value of `config_trig` is checked.
- If it is 0, the function call proceeds.
- Else, the function call returns with an error specifying a retrigger.
1. `dev_idx` is copied to the `config_device_idx` register on the host FPGA.
1. `addr` is copied to the `config_reg_addr` register on the host FPGA.
1. The `config_rw` register on the host FPGA is set to 0x00.
1. The `config_trig` register on the host FPGA is set to 0x01,
triggering configuration transmission by the firmware.
1. (Firmware) A configuration read is performed by the firmware.
1. (Firmware) `config_trig` is set to 0x00 by the firmware.
1. (Firmware) `CONFIGRACK` is pushed onto the signal stream by the
firmware.
1. The signal stream is pumped until either `CONFIGRACK` or `CONFIGRNACK` is
received indicating that the host FPGA has either:
- Completed reading the specified device register and copied its value to
the `config_reg_value` register.
- Failed to read the register in which case the value of `config_reg_value`
contains invalid data.
When a host requests a device register _write_, the following following actions
take place:
1. The value of `config_trig` is checked.
- If it is 0, the function call proceeds.
- Else, the function call returns with an error specifying a retrigger.
1. `dev_idx` is copied to the `config_device_id` register on the host FPGA.
1. `addr` is copied to the `config_reg_addr` register on the host FPGA.
1. `value` is copied to the `config_reg_value` register on the host FPGA.
1. The `config_rw` register on the host FPGA is set to 0x01.
1. The `config_trig` register on the host FPGA is set to 0x01, triggering
configuration transmission by the firmware.
1. (Firmware) A configuration write is performed by the firmware.
1. (Firmware) `config_trig` is set to 0x00 by the firmware.
1. (Firmware) `CONFIGWACK` is pushed onto the signal stream by the
firmware.
1. The signal stream is pumped until either `CONFIGWACK` or `CONFIGWNACK` is
received indicating that the host FPGA has either:
- Successfully completed writing the specified device register
- Failed to write the register
Following successful or unsuccessful device register read or write, the
appropriate ACK or NACK packets _must_ be passed to the [signal
channel](#sig-chan). If they are not, the register read and write calls will
block indefinitely.
### Global acquisition registers
The following global acquisition registers provide information about, and
control over, the entire acquisition system:
- `uint32_t running`: set to > 0 to run the system clock and produce data. Set
to 0 to stop the system clock and therefore stop data flow. Results in no
other configuration changes.
- `uint32_t reset`: set to > 0 to trigger a hardware reset and send a fresh
device map to the host and reset hardware to its default state. Set to 0 by
host firmware upon entering the reset state.
- `uint32_t sys_clock_hz`: A read-only register specifying the master (clock
domain 0) hardware clock frequency in Hz. The clock counter in the read
[frame](#frame) header is incremented at this frequency.
- `uint32_t version_selected_port`: This register selects which hardware hub will the two
vesion registers refer to. A valie of 0 will select the host hardware, while any other
will select the hub connected to that port.
- `uint32_t hardware_version`: Split in two uint16_t words. The higher word is a hardware ID
identifying common hubs (e.g.: an open-ephys headstage). The lower word contains the hardware
revision version.
- `uint32_t firmware_version`: Split in two uint16_t words. The higher word is the firmware version.
The second word is an optional value that, if the hardware supports online configuration, will
hold the version of the loaded firmware, or 0 otherwise.
## Data read channel {#data-rd-chan}
- __Word size__ : 32 bits
- __R/W Timing__ : Asynchronous
- __Read Only__
The _data read_ channel provides high bandwidth communication from the FPGA
firmware to the host computer using direct memory access (DMA). From the host's
perspective, its behavior is equivalent to a read-only, blocking UNIX named
pipe with the exception that data can only be read on 32-bit, instead of 8-bit,
boundaries. The data input channel communicates with the host using
[frames](#frame) with a read-header ("read-frames"). Read-frames are pushed
into the data input channel at a rate dictated by the FPGA firmware. It is
incumbent on the host to read this stream fast enough to prevent buffer
overflow. At the time of this writing, a typical implementation will allocate
an input buffer that occupies a 512 MB segment of kernal RAM. Increased
bandwidth demands will necessitate the creation of a user-space buffer. This
change shall have no effect on the API.
## Data write channel {#data-wr-chan}
- __Word size__ : 32 bits
- __R/W Timing__ : Asynchronous
- __Write Only__
The _data write_ channel provides high bandwidth communication from the host
computer to the FPGA firmware using DMA via calls. From the host's perspective,
its behavior is equivalent to a write-only, blocking UNIX named pipe with the
exception that data can only be written on 32-bit, instead of 8-bit,
boundaries. Its performance characteristics are largely identical to the data
input channel. However, the format of data written to the this channel is
different. Instead of writing frames with header information describing the
frame contents, data should be written to a single device at a time. This
action is very similar to a UNIX `write` syscall with some additional
arguments: namely the file descriptor is replaced by a context and device
index:
```
// UNIX write
size_t write(int fd, const void *buf, size_t count);
// Possible ONI write
size_t oni_write(const oni_ctx ctx, size_t dev_idx, const void *data, size_t data_sz)
```
\newpage
\newpage
# Required API Types and Behavior {#api-spec}
In the following sections we define required API datatypes and how they are
used by the API to communicate with hardware. An implementation of this API,
[liboepcie](#api-imp), follows.
## Context
A _context_ shall hold all state required to manage single [FPGA/Host
communication system](#comm-protocol). This includes a device map (simple list
of _devices_) being acquired from, data buffering elements, etc. API calls will
typically take a context handle as the first argument and use it to reference
required state information to enable communication and/or to mutate the context
to reflect some function side effect (e.g. add device map information):
```
int api_function(context *ctx, ...);
```
## Device
A _device_ is defined as configurable piece of hardware with its own register
address space (e.g. an integrated circuit) or something programmed within the
firmware to emulate this (e.g. an electrical stimulation sub-circuit made to
behave like a Master-8). Multiple devices can coexist on a single _hub_ that
is connected to the host through a single interface link. Host interaction
with a device is facilitated using a device description, which holds the following elements:
- `device_id`: Device ID number
- `port`: The index of the port the hub containing this device is connected to
- `clock_dom`: Device clock domain (0 is master, 1 or greater are slaves
synchronized to master)
- `clock_hz`: Clock rate in Hz of clock governing `clock_dom`
- `read_active`: Device will use the data input channel if read_size > 0 and it
has valid data.
- `read_size`: Device data read size per frame in bytes
- `num_reads`: Number of frames that must be read to construct a full
sample (e.g., for row reads from camera)
- `write_size`: Device data write size per frame in bytes
- `num_writes`: Number of frames that must be written to construct a full
output sample (e.g., for row writes for a display)
An array of structures holding each of these entries forms a _device map_. A
_context_ is responsible for managing a single device map, which keep track of
where to send and receive streaming data and configuration information during
API calls. A detailed description of each of each value comprising a device
instance is as follows:
1. `device_id` [RO]: Device identification number which is globally
enumerated for the entire project
- There shall be a single enumeration for a ONI library implementation that
uniquely identifies all possible devices that are controlled across _context_
configurations. This enumeration will grow with the number of devices
supported by the library.
- e.g. A host board GPIO subcircuit is could be 0, Intan RHD2132 is 1, Intan
RHD2164 is 2, etc.
2. `port` [RO]: the port the hub containing this device is connected to
- The possible available ports are determined by the host hardware
2. `clock_dom` [RO]: The clock domain that the device is sychronized to.
- All devices exist in a single clock domain
- There are one or more clock domains per device_map
3. `clock_hz` [RO]: The clock rate in Hz of the clock governing `clock_dom`
4. `read_active` [RW]: If not 0, the Device will use the data input channel if read_size > 0 and it
has valid data.
- This value can be changed to prevent the device from using data
input channel resources
- It is a device-specific data gate that is independent of `read_size` and
`num_reads`
4. `read_size` [RO]: Number of bytes of data transmitted by this device during a
single read.
- 0 indicates that it does not send data.
5. `num_reads` [RO]: Number of reads required to construct a full device read sample
(e.g., number of columns when `read_size` corresponds to a single row of
pixels from a camera sensor)
6. `write_size` [RO]: Number of bytes accepted by the device during a single write
- 0 indicates that it does not send data.
7. `num_writes` [RO]: Number of writes required to construct a full device output
sample.
## Frame
A _frame_ is a flat byte array containing a single sample's worth of data for a
set (one to all) of devices within a device map. Data within frames is arranged
into three memory sectors as follows:
```
[32 byte header, // 1. Header
dev_0 idx, dev_1 idx, ... , dev_n idx, // 2. Device map indices
dev_0 data, dev_1 data, ... , dev_n data] // 3. Data
```
Each frame memory sector is described below:
1. Header
- Each frame starts with a 32-byte header
- For reading (firmware to host) operations, the header contains
- bytes 0-7: unsigned 64-bit integer holding system clock counter
- bytes 8-9: unsigned 16-bit integer indicating number of devices that
the frame contains data for
- byte 10: 8-bit integer specifying fame error state. frame error. 0 =
OK. 1 = data may be corrupt.
- bytes 11-32: reserved
- For writing (host to firmware) operations , the header contains
- bytes 0-32: reserved
2. Device map indices
- An array of unsigned 32-bit keys corresponding the device map captured by
the host during _context_ initialization
- The offset, size, and type information of the _i_th data block within the
`data` section of each frame is determined by examining the _i_th member
of the device map.
3. Data
- Raw data blocks from each device in the device map.
- The ordering of device-specific blocks is the same as the device index
within the _device map index_ portion of the frame
- The read/write size for each device-specific block is provided in the
device map
- If timing informaiton is passed in the data block, it should be specified
how to interpret it (using plain text).
# Example Headstage Serialization Protocol {#ser-protocol}
TODO: Link
\newpage
# Example Open Ephys ONI-compliant API: `liboni` {#api-imp }
TODO: Link
\newpage
# Device Drivers for libONI {#driver-protocol}
Drivers implements low level read and write operations to communicate with specific hardware using the [ONI specification](#comm-protocol), which defines which streams, configuration registers and communication protocol any ONI-compatible hardware must expose.
To be able to be used by libONI they need to:
- Implement all the functions featured in `onidriver.h`
- Be built into a dynamic library named onidriver-\<drivername\>.\<ext\> where _drivername_ is the unique driver name and _ext_ the dynamic library extension for the operating system.
- The built driver needs to be placed in a location accessible to the sytem linker. Usually next to the libONI library.
Most driver functions receive a `oni_driver_ctx` parameter. It is a generic pointer to the custom structure that the driver must implement to store all the internal states of a single device context.
## Types
### `oe_driver_ctx`
This type is a generic pointer to a custom structure containing all necessary parameters and status variables for the driver to work. The particular implementation of such structure is left to the driver.
### `oni_read_stream_t`
Avilable read-only streams as defined by the ONI specification.
```{.c}
typedef enum {
ONI_READ_STREAM_DATA,
ONI_READ_STREAM_SIGNAL
} oni_read_stream_t;
```
Currently only the [data](#data-rd-chan) and [signal](#sig-chan) streams are defined by the specification.
### `oni_write_stream_t`
Available write-only streams as defined by the ONI specification
```{.c}
typedef enum {
ONI_WRITE_STREAM_DATA
} oni_write_stream_t;
```
At the moment, only a single [data](#data-wr-chan) stream is defined by the specification.
### `oni_config_t`
Available [configuration channel registers](#conf-chan) as defined by the ONI specification
```{.c}
typedef enum {
ONI_CONFIG_DEVICE_IDX,
ONI_CONFIG_REG_ADDR,
ONI_CONFIG_REG_VALUE,
ONI_CONFIG_RW,
ONI_CONFIG_TRIG,
ONI_CONFIG_RUNNING,
ONI_CONFIG_RESET,
ONI_CONFIG_SYSCLK
} oni_config_t;
```
The actual implementation of those registers is left to the driver. All of them must be implemented, following the exact behavior described in the specification.
## Functions
Drivers must implement all functions defined in `onidriver.h` and follow their expected behavior.
## `oni_driver_create_ctx`
Creates the driver specific hardware context.
``` {.c}
oni_driver_ctx oni_driver_create_ctx()
```
### Returns `oe_driver_ctx`
A driver-specific context, or NULL if there was an error.
### Description
This function must allocate a structure with all the required parameters and status variables for driver use. This structure is passed to the main lib as a generic driver context pointer. If the context has public parameters, those can be set or read via the libONI functions `oni_set_driver_opt` and `oni_get_driver_opt` which, in turn, will call the appropriate driver functions.
## `oni_driver_destroy_ctx`
Terminate a driver context and free bound resources
``` {.c}
int oni_driver_destroy_ctx(oni_driver_ctx driver_ctx)
```
### Arguments
- `driver_ctx` driver context
### Returns `int`
- 0: Success
- Less than 0: `oe_error_t`
### Description
This function must free the memory used by the driver context as well as any allocated resources.
## `oni_driver_init`
Initialize a driver and open the device
```{.c}
int oni_driver_init(oni_driver_ctx driver_ctx, int device_index)
```
### Arguments
- `driver_ctx` driver context
- `device_index` 0-based index of the host device to be opened. -1 for automatic selection.
### Returns `int`
- 0: Success
- Less than 0: `oe_error_t`
### Description
Opens a host device and prepares the driver for IO operations. Multiple devices of the same hardware type, managed by the same driver, can be addressed bt the `device_index` argument. A value of -1 indicates that the driver should automatically select the first available device.
## `oni_driver_read_stream`
Reads data from a ONI read-only stream
```{.c}
int oni_driver_read_stream(oni_driver_ctx driver_ctx, oni_read_stream_t stream, void* data, size_t size)
```
### Arguments
- `driver_ctx` driver context
- `stream` ONI stream to read
- `data` buffer to store read data
- `size` amount of bytes to read
### Returns `int`
- 0: Success
- Less than 0: `oe_error_t`
### Description
Reads from one of the read-only streams definded by the ONI standard, implemented by the hardware. The specifics on how this read is made are left to the individual driver, as long as the call is blocking and on return the `data` buffer is filled with exactly `size` bytes or an error is issued.
## `oni_driver_write_stream`
Writes to a ONI write-only stream
```{.c}
int oni_driver_write_stream(oni_driver_ctx driver_ctx, oni_write_stream_t stream, const char* data, size_t size);
```
### Arguments
- `driver_ctx` driver context
- `stream` ONI stream to write
- `data` buffer storing data to write
- `size` amount of bytes to write
### Returns `int`
- 0: Success
- Less than 0: `oe_error_t
### Description
Writes to a write-only stream defined by the ONI standard, implemented by the hardware. The specifics on how this write is made are left to the individual driver, as long as the call is blocking and on return exasctly `size` bytes have been writen to the stream from the `data` buffer or an error is issued.
## `oni_driver_read_config`
Reads a configuration register
```{.c}
int oni_driver_read_config(oni_driver_ctx driver_ctx, oni_config_t config, oni_reg_val_t* value)
```
### Arguments
- `driver_ctx` driver context
- `config` configuration channel register to read
- `value` pointer to the variable that will hold the result
### Returns `int`
- 0: Success
- Less than 0: `oe_error_t
### Description
Reads from one of the [configuration channel registers](#conf-chan) implemented by the hardware, returning its current value.
## `oni_driver_write_config`
Reads a configuration register
```{.c}
int oni_driver_write_config(oni_driver_ctx driver_ctx, oni_config_t config, oni_reg_val_t value)
```
### Arguments
- `driver_ctx` driver context
- `config` configuration channel register to write
- `value` value to write in the register
### Returns `int`
- 0: Success
- Less than 0: `oe_error_t
### Description
Writes to one of the [configuration channel registers](#conf-chan) implemented by the hardware.
## `oni_driver_set_opt`
Sets a driver specific context option
```{.c}
int oni_driver_set_opt(oni_driver_ctx driver_ctx, int driver_option, const void* value, size_t option_len)
```
### Arguments
- `driver_ctx` driver context
- `driver_option` driver option to be written
- `value` value to be written to the selected option
- `option_len` length of `value` in bytes
### Returns `int`
- 0: Success
- Less than 0: `oe_error_t
### Description
Sets a low-level driver option. This function can be called from the user application through libONI's `oni_set_driver_opt`. The possible options and their meanings are left to the individual drivers.
## `oni_driver_get_opt`
Gets a driver specific context option
```{.c}
int oni_driver_get_opt(oni_driver_ctx driver_ctx, int driver_option, void* value, size_t* option_len)
```
### Arguments
- `driver_ctx` driver context
- `driver_option` driver option to be read
- `value` buffer to store value of `option`
- `option_len` pointer to the size of `value`(including terminating null character, if applicable) in bytes
### Returns `int`
- 0: Success
- Less than 0: `oe_error_t
### Description
Reads a low-level driver option. This function can be called from the user application through libONI's `oni_get_driver_opt`. The possible options and their meanings are left to the individual drivers.
## `oni_driver_set_opt_callback`
Callback for API option set
```{.c}
int oni_driver_set_opt_callback(oni_driver_ctx driver_ctx, int oni_option, const void* value, size_t option_len)
```
### Arguments
- `driver_ctx` driver context
- `option` main API option written
- `value` value that was written to the selected option
- `option_len` length of `value` in bytes
### Returns `int`
- 0: Success
- Less than 0: `oe_error_t
### Description
This function is called by the API when a global context option is set. Calling this function is the last task libOBI's `oni_set_opt`, returning its return value to the calling user application.
Its intended use is for the driver to react, if needed, to any global option change.
## `driver_get_id`
Returns the driver identification string
```{.c}
const char* oni_driver_get_id()
```
### Returns `const char*`
A string containing the driver name
### Description
Returns a string containing the driver name. This name must be the same as the one in the filename of the driver library.
\newpage
# Notes for version 0.4
1. Support for multiple data input and output channels
- The input or output channel used by a device should be part of its descriptor
- The `slot` member was added for this, but its functionality needs to be tested
2. Add device silencing to _context_ options
- User should be able to shut off particular devices to prevent them from
using the input channel
- This can be accomplished via very similar logic to RUNNING -- it will
interact wwith the data input controller at the same points, but in a
device-specific manner.
3. Add a read-only global register with hardware revision version and firmware
version. Or, when we get reconfiguration working, static firmware version and
loaded firmware version. This is especially important for reconfiguration, as
we might need different bitfiles for different versions in the future. In any
case having information and a way of knowing which board is being used never
hurts.
4. The whole concept of "official" device IDs might be beyond the intended
scope of this spec. Instead, maybe devices should only berequired to report
frame read/write sizes in device map. Having official IDs can be an
implementation choice. I've moved the wording I'm referring to to here:
- Device IDs up to 9999 are reserved. Device ID 10000 and greater are free
to use for custom hardware projects.
- The use of device IDs less than 10000 not specified within this
enumeration will result in OE_EDEVID errors.
- Device numbers greater than 9999 are allowed for general purpose use and
will not be verified by the API.
- Incorporation into the official device enum (device IDs < 10000) can be
achieved via pull-request to the ONI repository.