libxtos-bci
BCI sessions, device capability, buffer ownership, and released data-format limits.
On this page
Device prerequisites
A session accesses an XTOS character device such as /dev/xtos0, not an arbitrary /dev/ttyUSB0.
Enable xtos_bci_core, xtos_bci_buffer, and the matching device driver, then register the device.
The public OpenBCI Cyton implementation is a serdev driver with device-tree compatible openbci,cyton; loading modules does not establish binding or registration.
create calls open(path, O_RDWR), so the caller needs read/write permission. A missing node usually gives ENOENT; insufficient permission gives EACCES. The current core permits one opener; a second gets EBUSY. QEMU can check module loading, not hardware acquisition without the board.
Session order
xtos_bci_session_t *xtos_bci_session_create(const char *device_path);
int xtos_bci_get_info(xtos_bci_session_t *session,
struct xtos_bci_device_info *info);
int xtos_bci_start(xtos_bci_session_t *session);
int xtos_bci_stop(xtos_bci_session_t *session);
void xtos_bci_session_destroy(xtos_bci_session_t *session);- create: path is non-NULL and NUL-terminated; NULL gives EINVAL, allocation/open failure returns NULL with the failing errno.
- get_info: session/info are non-NULL; returns the cache from create and does not issue GET_INFO again. Check num_channels, sampling_rate, and resolution_bits.
- Configure: choose driver-supported rate and channels before acquisition; success does not prove hardware adopted every setting.
- start→acquire or poll→stop: start/stop issue ioctl and return 0/-1; streaming changes only after ioctl success.
- destroy: stop workers first; it attempts to stop an active session, then munmap, close, and free. destroy(NULL) is a no-op and cleanup failures are not returned.
If GET_INFO fails, create still returns a session with a calloc-zeroed cache; later get_info can return 0. Successful creation does not prove XTOS UAPI. info.name is a fixed 32-byte array and may lack NUL termination; print it with a bound. session.c has no concurrency lock; do not race reads, configuration, or destruction.
Acquisition controls
| Interface | Constraints and return | Public Cyton behavior |
|---|---|---|
| set_sampling_rate | rate is integer Hz; the library does not check the supported list; 0/-1 and cache updates on success. | Supports 250/500/1000/2000 Hz, else EINVAL; serial-command result is not fully checked and the existing ring buffer is not resized. |
| set_channel | channel is zero-based, enabled is bool, gain is uint32; NULL session is EINVAL, otherwise returns ioctl. | Cyton uses 0–7; core forwards channel/enabled but not gain, so hardware gain is not changed. |
| calibrate | NULL session gives -1/EINVAL; other results depend on the driver. | Cyton prints not implemented and returns 0 without calibrating. |
| check_impedance | Non-NULL output, 1≤channels≤32; array has at least channels uint32 values, in integer kΩ. | Cyton returns EOPNOTSUPP; do not invent values. |
These functions configure acquisition devices; they are not generic actuator commands. ioctl can return EINVAL, EOPNOTSUPP, EFAULT, or ENOTTY for an unknown command, and serial errors can propagate. Distinguish invalid parameters, unsupported capability, and link failure; record requested and verified configuration.
Sample format
ssize_t xtos_bci_read_samples(xtos_bci_session_t *session,
float *buffer, size_t num_samples);Requires non-NULL session/buffer and num_samples>0, otherwise -1/EINVAL.
One read requests num_samples×sizeof(float) bytes and returns bytes/sizeof(float); buffer holds at least num_samples floats.
The parameter counts scalar channel values, not automatically multichannel frames.
Short reads are allowed; read supplies errno on -1; multiplication overflow and frame alignment are unchecked.
The read interface returns no timestamp, packet sequence, quality flags, or reliable frame boundary; read completion is not sample time. If a verified driver is read through the borrowed fd, the application must implement byte assembly, frame alignment, unit conversion, and timing; the SDK does not provide this. The public serial parser reads channel bytes from packet[1], does not explicitly extract a sequence, and lacks complete trailer validation. Source presence does not verify the Cyton protocol.
fd and mmap
void *xtos_bci_get_mmap_buffer(xtos_bci_session_t *session, size_t *size);
int xtos_bci_get_fd(xtos_bci_session_t *session);get_fd returns the session-owned borrowed fd; NULL gives -1/EINVAL. It can be used with poll/select, but do not close it or use it after destroy. open does not set O_NONBLOCK; read after poll still needs race, signal, and short-read handling. fcntl can enable nonblocking mode, where no data can give EAGAIN.
get_mmap_buffer estimates channels×sampling_rate×sizeof(float)×2 bytes and maps PROT_READ, MAP_SHARED; size may be NULL. Success caches and returns the same borrowed address. Failure returns NULL with mmap or validation errno. Do not free/munmap it; destroy releases it; do not read size after failure.
The estimate does not query actual ring-buffer bytes, and a rate change does not refresh the cached mapping. mmap uses pages; a non-page-aligned or stale size can exceed the buffer and fail. The mapping has no write_pos/read_pos, stable snapshot, timestamp, or consumer-advance protocol; a pointer alone is not a zero-copy stream.
Statistics and limits
int xtos_bci_get_stats(xtos_bci_session_t *session,
struct xtos_bci_statistics *stats);
int xtos_bci_reset_stats(xtos_bci_session_t *session);get_stats requires non-NULL session/stats and returns 0/-1; reset_stats requires non-NULL session. Fields are samples_read, samples_dropped, errors, and overruns. Cyton increments samples_read when channel values enter the buffer, not when an application consumes a frame. When space is short, the ring buffer drops oldest bytes and increments overruns; samples_dropped is not a complete loss count. Sources: sdk/libxtos-bci/include/xtos/bci.h, bci_uapi.h, src/session.c, and kernel xtos_bci_core.c, xtos_bci_buffer.c, devices/openbci_cyton.c.
Record session control, missing read conversion, placeholder calibration, gain not forwarded to the driver, unsupported impedance, and the incomplete mmap protocol separately. They do not establish a verified end-to-end hardware BCI path.