Commit 3d8e74e7 authored by Alessandro Rubini's avatar Alessandro Rubini

Merge branch 'lib-overhaul'

This set of commits completely reviews and simplifies the API, fixing
the sequence of allocation/fill/deallocation for buffers.
parents fdd253a2 5cb69abd
......@@ -2,7 +2,7 @@
.PHONY: all clean modules install modules_install
.PHONY: gitmodules prereq prereq_install prereq_install_warn
DIRS = kernel tools lib libtools
DIRS = kernel tools lib libtools libtests
all clean modules install modules_install: gitmodules
for d in $(DIRS); do $(MAKE) -C $$d $@ || exit 1; done
......
......@@ -35,7 +35,7 @@
@setchapternewpage off
@set update-month June 2013
@set update-month July 2013
@finalout
......@@ -65,13 +65,6 @@ David Cobas, Federico Vaga and Alessandro Rubini in March 2013. Ideas
have later been refined during real use. Alessandro wrote this
document.
@b{Warning}: this file documents the library as we want it to be
when we release it. Currently, the prototypes match documentation
but the semantics are still old-fashioned (they match the first
development implementation, that revealed suboptimal).
I'll remove this warning when I'm done with porting the code to the new
conventions.
@c ##########################################################################
@node General Ideas and Rationale
@chapter General Ideas and Rationale
......@@ -122,7 +115,7 @@ The choice of arrays is driven by the need for generic structures
that can be used unchanged with different hardware cards.
While there is a little overhead in parsing the generic structures,
configuration is not something that happens in hot paths, and wr see
configuration is not something that happens in hot paths, and we see
no problems in that. Also, please note that setting the configuration
and applying it are different steps, and the configuration data structures
can be prepared and saved for later use.
......@@ -200,11 +193,11 @@ may release it in future versions.
@findex fmcadc_close
@example
fmcadc_dev *fmcadc_open(char *name, unsigned int dev_id,
unsigned long totalsize,
unsigned long totalsamples,
unsigned int nbuffer,
unsigned long flags)
struct fmcadc_dev *fmcadc_open_by_lun(char *name, int lun,
unsigned long totalsize,
unsigned long totalsamples,
unsigned int nbuffer,
unsigned long flags);
extern int fmcadc_close(struct fmcadc_dev *dev);
......@@ -222,7 +215,7 @@ This is the meaning of the various arguments:
@item name
Devices are opened by name, and the name for the only supported
card at the moment is ``@t{fmc-adc-100m}''.
card at the moment is ``@t{fmc-adc-100m14b4cha}''.
@item dev_id
......@@ -235,13 +228,14 @@ This is the meaning of the various arguments:
Logical Unit Number. This number comes from a database description
of the system (@i{open_by_lun} is not currently implemented).
@item totalsize
@item totalsamples
This is a a hint about how big a buffer the application will use.
Managing big acquisitions (hundreds of megabytes, or gigabytes)
requires some pre-allocation of the data, and sometimes this
configuration happens at device level, so it's good to have
the information at open time.
the information at open time. This is a number of samples,
i.e. @t{nshots * (presamples + postsamples)}, not a number of bytes.
@item nbuffer
......@@ -266,9 +260,9 @@ The timestamp structrure is defined as follows:
@smallexample
struct fmcadc_timestamp {
uint64_t secs;
uint64_t ticks;
uint64_t bins;
uint64_t secs;
uint64_t ticks;
uint64_t bins;
};
@end smallexample
......@@ -408,12 +402,14 @@ of the hardware), as well as other informative fields:
@smallexample
struct fmcadc_buffer {
void *data;
void *metadata;
int samplesize;
int nsamples;
char *drivername;
unsigned long flags; /* internal to the library */
void *data;
void *metadata;
int samplesize;
int nsamples;
struct fmcadc_dev *dev;
unsigned long flags; /* internal to the library */
void *mapaddr;
unsignef long maplen;
};
@end smallexample
......@@ -429,11 +425,11 @@ The following functions are related to buffers:
struct fmcadc_buffer *fmcadc_request_buffer(struct fmcadc_dev *dev,
int nsamples,
void *(*alloc_fn)(size_t),
unsigned int flags,
struct timeval *timeout);
unsigned int flags);
int fmcadc_fill_buffer(struct fmcadc_dev *dev,
struct fmcadc_buffer *buf,
unsigned int flags);
unsigned int flags,
struct timeval *timeout);
struct fmcadc_timestamp *fmcadc_tstamp_buffer(struct fmcadc_buffer *buf,
struct fmcadc_timestamp *ts);
int fmcadc_release_buffer(struct fmcadc_dev *dev,
......@@ -498,17 +494,18 @@ they appear:
Currently unused, available for future special cases.
@item buf
The buffer pointer, as returned by the allocation function.
@item timeout
The timeout for allocation is used like the timeout in @i{select(2)}.
The timeout for filling the buffer is used like
the timeout in @i{select(2)}.
If NULL, there is no timeout, if set, it is used as a maximum waiting
time. If allocation times out, @t{NULL} is returned, with
@t{-ETIMEDOUT} as @i{errno}.
@item buf
The buffer pointer, as returned by the allocation function.
@item ts
A pointer to timestamp, so the library can copy the timestamp
......@@ -545,7 +542,7 @@ To be documented in a later version.
During June 2013 we defined the final API as documented here. However,
code written before June 28th may use the initial conventions, that are
no more active. The current API is designed to be persistent over
the yeears, and work with any ADC card belonging to the same family
the years, and work with any ADC card belonging to the same family
as our first board, the ``fmc-adc-100m14b4cha'' one.
This is the list of incompatibilities. I list the commit, so you
......
......@@ -1058,14 +1058,6 @@ other devices, when they become available.
The library, also called API in some contexts, is described by a separate
document, because over time we expect it to live in its own package.
@b{Warning}: the library code, as included in this version, is not
consistent with documentation: the prototypes match, but the semantics
are different (during June 2013 we refined the API and the new, semantics,
that will remain over time, are not yet implemented as of today).
The tools work as advertised, but not according the documented behavior
of the library. When everything is fixed, I'll remove this warning,
so the document always matches the code it is distributed with.
@c ##########################################################################
@node Library-based Tools
@chapter Library-based Tools
......@@ -1084,11 +1076,6 @@ All library-based tools filenames use @t{fald-} as a prefix, for
@node Simple Acquisition
@subsection Simple Acquisition
@b{Warning}: while the program works as documented, but the source
code uses a non-compliant version of the library. Please don't look
at the source at this point. I'll remove this warning when the
library is fixed.
The most important tool is @file{fald-simple-acq}, which perform
and ADC acquisition according to the specified parameters. The source code
is meant to be used as an example by application authors, but sometimes
......@@ -1263,15 +1250,23 @@ from the char devices. This allows reading directly with @i{zio-dump}
or other programs, while still using @i{fald-simple-acq} to configure
the acquisition.
@c ==========================================================================
@node Test Program
@subsection Test Program
The program called @t{fald-test} is a very simple test program,
that can allocate several buffers and fill them all, or use a single
buffer over and over for multi-shot acquisition.
It is not documented for lack of time, but the source is meant to be
readable. We used it and @i{strace} to check that stuff happens
properly as documented. Configuration is done mainly by setting
environment variables.
@c ==========================================================================
@node Retrieve Configuration
@subsection Retrieve Configuration
@b{Warning}: while the program is expected to work as documented, but
the source code uses a non-compliant version of the library. Please
don't look at the source at this point. I'll remove this warning when
the library is fixed.
To retrieve the current configuration from a device you can use the
demo program @file{fald-simple-get-conf}:
......
......@@ -3,6 +3,7 @@ ZIO ?= ../zio
LIB = libfmcadc.a
LOBJ := route.o
LOBJ += init.o
LOBJ += boards.o
LOBJ += config-zio.o
LOBJ += buffer-zio.o
......
......@@ -31,20 +31,28 @@
#define FMCADC_ZIO_BRD_MASK (1LL << FMCADC_CONF_BRD_STATE_MACHINE_STATUS) | \
(1LL << FMCADC_CONF_BRD_N_CHAN)
struct fmcadc_op fa_100ms_4ch_14bit_op = {
.open = fmcadc_zio_open,
.open_by_lun = fmcadc_zio_open_by_lun,
.close = fmcadc_zio_close,
.start_acquisition = fmcadc_zio_start_acquisition,
.stop_acquisition = fmcadc_zio_stop_acquisition,
.apply_config = fmcadc_zio_apply_config,
.retrieve_config = fmcadc_zio_retrieve_config,
.request_buffer = fmcadc_zio_request_buffer,
.release_buffer = fmcadc_zio_release_buffer,
struct fmcadc_operations fa_100ms_4ch_14bit_op = {
.open = fmcadc_zio_open,
.close = fmcadc_zio_close,
.acq_start = fmcadc_zio_acq_start,
.acq_poll = fmcadc_zio_acq_poll,
.acq_stop = fmcadc_zio_acq_stop,
.apply_config = fmcadc_zio_apply_config,
.retrieve_config = fmcadc_zio_retrieve_config,
.get_param = fmcadc_zio_get_param,
.set_param = fmcadc_zio_set_param,
.request_buffer = fmcadc_zio_request_buffer,
.fill_buffer = fmcadc_zio_fill_buffer,
.tstamp_buffer = fmcadc_zio_tstamp_buffer,
.release_buffer = fmcadc_zio_release_buffer,
};
struct fmcadc_board_type fmcadc_100ms_4ch_14bit = {
.name = "fmcadc_100MS_4ch_14bit",
.devname = "adc-100m14b",
.name = "fmc-adc-100m14b4cha", /* for library open() */
.devname = "adc-100m14b", /* for device named in /dev/zio */
.driver_type = "zio",
.capabilities = {
FMCADC_ZIO_TRG_MASK,
......@@ -55,83 +63,65 @@ struct fmcadc_board_type fmcadc_100ms_4ch_14bit = {
.fa_op = &fa_100ms_4ch_14bit_op,
};
/* fmcadc_open
* @name: name of the device type to open
* @dev_id: device identificator of a particular device connected to the system
/*
* The following array is the main entry point into the boards
*/
static const struct fmcadc_board_type *fmcadc_board_types[] = {
&fmcadc_100ms_4ch_14bit,
/* add new boards here */
};
static const struct fmcadc_board_type *find_board(char *name)
{
int i;
for (i = 0; i < ARRAY_SIZE(fmcadc_board_types); i++)
if (!strcmp(name, fmcadc_board_types[i]->name))
return fmcadc_board_types[i];
errno = ENODEV;
return NULL;
}
/* Open should choose the buffer type (FIXME) */
struct fmcadc_dev *fmcadc_open(char *name, unsigned int dev_id,
unsigned long buffersize,
unsigned int nbuffer,
unsigned long flags)
{
struct fmcadc_dev *dev = NULL;
int i, found = 0;
const struct fmcadc_board_type *b;
/* name cannot be NULL */
if (!name)
b = find_board(name);
if (!b)
return NULL;
/* Look in the list of supported board if the "name" board is there */
for (i = 0; i < __FMCADC_SUPPORTED_BOARDS_LAST_INDEX; ++i) {
if (!strcmp(name, fmcadc_board_types[i]->name)) {
found = 1;
break;
}
}
if (!found) {
errno = ENODEV;
return NULL; /* Not found */
}
/* The library supports this board */
if (fmcadc_board_types[i]->fa_op && fmcadc_board_types[i]->fa_op->open) {
dev = fmcadc_board_types[i]->fa_op->open(fmcadc_board_types[i],
dev_id, flags);
} else {
errno = FMCADC_ENOP;
}
return dev;
return b->fa_op->open(b, dev_id, buffersize, nbuffer, flags);
}
/*
* fmcadc_open_by_lun
* @name: name of the device type to open
* @lun: Logical Unit Number of the device
*
* TODO
*/
/* Open by lun should lookup a database */
struct fmcadc_dev *fmcadc_open_by_lun(char *name, int lun,
unsigned long buffersize,
unsigned int nbuffer,
unsigned long flags)
unsigned long buffersize,
unsigned int nbuffer,
unsigned long flags)
{
if (!name)
const struct fmcadc_board_type *b;
b = find_board(name);
if (!b)
return NULL;
/*
* FIXME: open_by_lun should lookup the database and get dev_id
* return b->fa_op->open( .... dev_id ...);
*/
errno = ENOSYS;
return NULL;
}
/*
* fmcadc_close
* @dev: the device to close
*/
int fmcadc_close(struct fmcadc_dev *dev)
{
struct fmcadc_gid *b = (void *)dev;
if (!dev) {
/* dev cannot be NULL */
errno = EINVAL;
return -1;
}
if (b->board->fa_op->close) {
return b->board->fa_op->close(dev);
} else {
errno = FMCADC_ENOP;
return -1;
}
return b->board->fa_op->close(dev);
}
......@@ -16,6 +16,7 @@
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/select.h>
......@@ -24,123 +25,199 @@
#include "fmcadc-lib.h"
#include "fmcadc-lib-int.h"
struct zio_control *fmcadc_zio_read_ctrl(struct __fmcadc_dev_zio *fa)
/* Internal function to read the control, already allocated in the buffer */
static int fmcadc_zio_read_ctrl(struct __fmcadc_dev_zio *fa,
struct fmcadc_buffer *buf)
{
struct zio_control *ctrl;
int i;
ctrl = malloc(sizeof(struct zio_control));
if (!ctrl)
return NULL;
i = read(fa->fdc, buf->metadata, sizeof(struct zio_control));
i = read(fa->fdc, ctrl, sizeof(struct zio_control));
switch (i) {
case sizeof(struct zio_control):
return 0; /* ok */
case -1:
fprintf(stderr, "control read: %s\n",
strerror(errno));
return NULL;
if (fa->flags & FMCADC_FLAG_VERBOSE)
fprintf(stderr, "%s: read: %s\n", __func__,
strerror(errno));
return -1;
case 0:
fprintf(stderr, "control read: unexpected EOF\n");
return NULL;
if (fa->flags & FMCADC_FLAG_VERBOSE)
fprintf(stderr, "%s: unexpected EOF\n", __func__);
return -1;
default:
fprintf(stderr, "ctrl read: %i bytes (expected %zi)\n",
i, sizeof(ctrl));
return NULL;
case sizeof(struct zio_control):
break; /* ok */
if (fa->flags & FMCADC_FLAG_VERBOSE)
fprintf(stderr, "%s: read: %i bytes (expected %zi)\n",
__func__, i, sizeof(ctrl));
return -1;
}
return ctrl;
}
static void fmcadc_zio_release_ctrl(struct zio_control *ctrl)
{
free(ctrl);
}
static void *fmcadc_zio_read_data(struct __fmcadc_dev_zio *fa,
unsigned int datalen)
/* Internal function to read or map the data, already allocated in the buffer */
static int fmcadc_zio_read_data(struct __fmcadc_dev_zio *fa,
struct fmcadc_buffer *buf)
{
void *data;
struct zio_control *ctrl = buf->metadata;
int datalen;
int samplesize = buf->samplesize; /* Careful: includes n_chan */
int i;
data = malloc(datalen);
if (!data)
return NULL;
/* we allocated buf->nsamples, we can have more or less */
if (buf->nsamples < ctrl->nsamples)
datalen = samplesize * buf->nsamples;
else
datalen = samplesize * ctrl->nsamples;
if (fa->flags & FMCADC_FLAG_MMAP) {
unsigned long mapoffset = ctrl->mem_offset;
unsigned long pagemask = fa->pagesize - 1;
if (buf->mapaddr) /* unmap previous block */
munmap(buf->mapaddr, buf->maplen);
buf->maplen = (mapoffset & pagemask) + datalen;
buf->mapaddr = mmap(0, buf->maplen, PROT_READ, MAP_SHARED,
fa->fdd, mapoffset & ~pagemask);
if (buf->mapaddr == MAP_FAILED)
return -1;
buf->data = buf->mapaddr + (mapoffset & pagemask);
return 0;
}
i = read(fa->fdd, data, datalen);
if (i > 0 && i <= datalen){
if (i < datalen)
fprintf(stderr, "data read: %i bytes (expected %i)\n",
i, datalen);
return data;
} else {
if (i == 0)
fprintf(stderr, "data read: unexpected EOF\n");
else
fprintf(stderr, "data read: %s\n", strerror(errno));
return NULL;
/* read */
i = read(fa->fdd, buf->data, datalen);
if (i == datalen)
return 0;
if (i > 0) {
if (fa->flags & FMCADC_FLAG_VERBOSE)
fprintf(stderr, "%s: read %i bytes (exp. %i)\n",
__func__, i, datalen);
buf->nsamples = i / fa->samplesize;
/* short read is allowed */
return 0;
}
if (i == 0) {
if (fa->flags & FMCADC_FLAG_VERBOSE)
fprintf(stderr, "%s: unexpected EOF\n", __func__);
errno = ENODATA;
return -1;
}
if (fa->flags & FMCADC_FLAG_VERBOSE)
fprintf(stderr, "%s: %s\n", __func__, strerror(errno));
return -1;
}
static void fmcadc_zio_release_data(void *data)
{
free(data);
}
/* externally-called: malloc buffer and metadata, do your best with data */
struct fmcadc_buffer *fmcadc_zio_request_buffer(struct fmcadc_dev *dev,
int nsamples,
void *(*alloc)(size_t),
unsigned int flags,
struct timeval *timeout)
unsigned int flags)
{
struct __fmcadc_dev_zio *fa = to_dev_zio(dev);
struct fmcadc_buffer *buf;
struct zio_control *ctrl;
void *data;
fd_set set;
int err;
unsigned int len;
char s[16];
/* If this is the first buffer, we need to know which kind it is */
if ((fa->flags & (FMCADC_FLAG_MALLOC | FMCADC_FLAG_MMAP)) == 0) {
fmcadc_get_param(dev, "cset0/current_buffer", s, NULL);
if (!strcmp(s, "vmalloc"))
fa->flags |= FMCADC_FLAG_MMAP;
else
fa->flags |= FMCADC_FLAG_MALLOC;
}
buf = calloc(1, sizeof(*buf));
if (!buf)
if (!buf) {
errno = ENOMEM;
return NULL;
}
buf->metadata = calloc(1, sizeof(struct zio_control));
if (!buf->metadata) {
free(buf);
errno = ENOMEM;
return NULL;
}
/* Allocate data: custom allocator, or malloc, or mmap */
if (!alloc && fa->flags & FMCADC_FLAG_MALLOC)
alloc = malloc;
if (alloc) {
buf->data = alloc(nsamples * fa->samplesize);
if (!buf->data) {
free(buf->metadata);
free(buf);
errno = ENOMEM;
return NULL;
}
} else {
/* mmap is done later */
buf->data = NULL;
}
/* Copy other information */
buf->samplesize = fa->samplesize;
buf->nsamples = nsamples;
buf->dev = (void *)&fa->gid;
buf->flags = flags;
return buf;
}
int fmcadc_zio_fill_buffer(struct fmcadc_dev *dev,
struct fmcadc_buffer *buf,
unsigned int flags,
struct timeval *timeout)
{
struct __fmcadc_dev_zio *fa = to_dev_zio(dev);
fd_set set;
int ret;
/* So, first sample and blocking read. Wait.. */
FD_ZERO(&set);
FD_SET(fa->fdc, &set);
err = select(fa->fdc + 1, &set, NULL, NULL, timeout);
if (err == 0) {
ret = select(fa->fdc + 1, &set, NULL, NULL, timeout);
if (ret == 0) {
errno = EAGAIN;
return NULL; /* no free, but the function is wrong generally */
} else if (err < 0) {
return NULL;
return -1;
} else if (ret < 0) {
return -1;
}
/* Ready to read */
ctrl = fmcadc_zio_read_ctrl(fa);
if (!ctrl)
goto out_ctrl;
len = (ctrl->nsamples * ctrl->ssize);
data = fmcadc_zio_read_data(fa, len);
if (!data)
goto out_data;
buf->data = data;
buf->metadata = (void *) ctrl;
return buf;
ret = fmcadc_zio_read_ctrl(fa, buf);
if (ret < 0)
return ret;
ret = fmcadc_zio_read_data(fa, buf);
if (ret < 0)
return ret;
return 0;
}
out_data:
fmcadc_zio_release_ctrl(ctrl);
out_ctrl:
return NULL;
struct fmcadc_timestamp *fmcadc_zio_tstamp_buffer(struct fmcadc_buffer *buf,
struct fmcadc_timestamp *ts)
{
struct zio_control *ctrl = buf->metadata;
if (ts) {
memcpy(ts, &ctrl->tstamp, sizeof(*ts)); /* FIXME: endianness */
return ts;
}
return (struct fmcadc_timestamp *)&ctrl->tstamp;
}
int fmcadc_zio_release_buffer(struct fmcadc_dev *dev,
struct fmcadc_buffer *buf,
void (*free_fn)(void *))
{
fmcadc_zio_release_ctrl(buf->metadata);
fmcadc_zio_release_data(buf->data);
struct __fmcadc_dev_zio *fa = to_dev_zio(dev);
free(buf->metadata);
if (!free_fn && fa->flags & FMCADC_FLAG_MALLOC)
free_fn = free;
if (free_fn)
free_fn(buf->data);
else if (buf->mapaddr && buf->mapaddr != MAP_FAILED)
munmap(buf->mapaddr, buf->maplen);
free(buf);
return 0;
}
......@@ -12,6 +12,7 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
......@@ -25,98 +26,135 @@
#define FMCADC_CONF_SET 1
/*
* __fa_zio_sysfs_get
* @path: path to the sysfs attribute
* @resp: value returned by the sysfs attribute
* Internal functions to read and write a string.
* Trailing newlines are added/removed as needed
*/
static int __fa_zio_sysfs_get(char *path, uint32_t *resp)
static int __fa_zio_sysfs_set(struct __fmcadc_dev_zio *fa, char *name,
char *val, int maxlen)
{
FILE *f = fopen(path, "r");
char pathname[128];
char newval[maxlen + 1];
int fd, ret, len;
snprintf(pathname, sizeof(pathname), "%s/%s", fa->sysbase, name);
len = sprintf(newval, "%s\n", val);
if (!f)
fd = open(pathname, O_WRONLY);
if (fd < 0)
return -1;
errno = 0;
if (fscanf(f, "%i", resp) != 1) {
fclose(f);
if (!errno)
errno = EINVAL;
ret = write(fd, newval, len);
close(fd);
if (ret < 0)
return -1;
}
fclose(f);
return 0;
if (ret == len)
return 0;
errno = EIO; /* short write */
return -1;
}
/*
* __fa_zio_sysfs_set
* @path: path to the sysfs attribute
* @value: value to set in the sysfs attribute
*/
static int __fa_zio_sysfs_set(char *path, uint32_t *value)
static int __fa_zio_sysfs_get(struct __fmcadc_dev_zio *fa, char *name,
char *val /* no maxlen: reader knows */ )
{
char s[16];
int fd, ret, len;
char pathname[128];
int fd, ret;
len = sprintf(s, "%i\n", *value);
fd = open(path, O_WRONLY);
snprintf(pathname, sizeof(pathname), "%s/%s", fa->sysbase, name);
fd = open(pathname, O_RDONLY);
if (fd < 0)
return -1;
ret = write(fd, s, len);
ret = read(fd, val, 128 /* well... user knows... */);
close(fd);
if (ret < 0)
return -1;
if (ret == len)
if (val[ret - 1] == '\n')
val[ret - 1] = '\0';
return 0;
}
/*
* Public functions (through ops and ./route.c).
* They manage both strings and integers
*/
int fmcadc_zio_set_param(struct fmcadc_dev *dev, char *name,
char *sptr, int *iptr)
{
struct __fmcadc_dev_zio *fa = to_dev_zio(dev);
char istr[12];
int len;
if (sptr)
return __fa_zio_sysfs_set(fa, name, sptr, strlen(sptr + 2));
len = sprintf(istr, "%i", *iptr);
return __fa_zio_sysfs_set(fa, name, istr, len + 2);
}
int fmcadc_zio_get_param(struct fmcadc_dev *dev, char *name,
char *sptr, int *iptr)
{
struct __fmcadc_dev_zio *fa = to_dev_zio(dev);
char istr[12];
int ret;
if (sptr)
return __fa_zio_sysfs_get(fa, name, sptr);
ret = __fa_zio_sysfs_get(fa, name, istr);
if (ret < 0)
return ret;
if (sscanf(istr, "%i", iptr) == 1)
return 0;
errno = EINVAL;
return -1;
}
/*
* fa_zio_sysfs_get
* @fa: device owner of the attribute
* @name: relative path to the sysfs attribute within the device
* @resp: value returned by the sysfs attribute
* Previous functions, now based on the public ones above
* Note: they are swapped: get, then set (above is set then get)
* FIXME: code using these must be refactored using data structures
*/
static int fa_zio_sysfs_get(struct __fmcadc_dev_zio *fa, char *name,
uint32_t *resp)
{
char pathname[128];
struct fmcadc_dev *dev = (void *)&fa->gid; /* hack: back and forth.. */
int ret;
int val;
sprintf(pathname, "%s/%s", fa->sysbase, name);
ret = __fa_zio_sysfs_get(pathname, resp);
ret = fmcadc_zio_get_param(dev, name, NULL, &val);
if (!ret) {
*resp = val; /* different type */
return 0;
}
if (!(fa->flags & FMCADC_FLAG_VERBOSE))
return ret;
/* verbose tail */
if (ret)
fprintf(stderr, "lib-fmcadc: Error reading %s\n", pathname);
fprintf(stderr, "lib-fmcadc: Error reading %s (%s)\n",
name, strerror(errno));
else
fprintf(stderr, "lib-fmcadc: %08x %5i <- %s\n",
(int)*resp, (int)*resp, pathname);
(int)*resp, (int)*resp, name);
return ret;
}
/*
* fa_zio_sysfs_set
* @fa: device owner of the attribute
* @name: relative path to the sysfs attribute within the device
* @value: value to set in the sysfs attribute
*/
int fa_zio_sysfs_set(struct __fmcadc_dev_zio *fa, char *name,
uint32_t *value)
{
char pathname[128];
struct fmcadc_dev *dev = (void *)&fa->gid; /* hack: back and forth.. */
int ret;
int val = *value; /* different type */
sprintf(pathname, "%s/%s", fa->sysbase, name);
ret = __fa_zio_sysfs_set(pathname, value);
ret = fmcadc_zio_set_param(dev, name, NULL, &val);
if (!ret)
return 0;
if (!(fa->flags & FMCADC_FLAG_VERBOSE))
return ret;
/* verbose tail */
if (ret)
fprintf(stderr, "lib-fmcadc: Error writing %s\n", pathname);
fprintf(stderr, "lib-fmcadc: Error writing %s (%s)\n",
name, strerror(errno));
else
fprintf(stderr, "lib-fmcadc: %08x %5i -> %s\n",
(int)*value, (int)*value, pathname);
(int)*value, (int)*value, name);
return ret;
}
......
......@@ -26,57 +26,39 @@
#include "fmcadc-lib.h"
#include "fmcadc-lib-int.h"
#define ZIO_DEV_PATH "/dev/zio"
#define ZIO_SYS_PATH "/sys/bus/zio/devices"
#define FMCADC_NCHAN 4
/* * * * * * * * * * Library Operations Implementation * * * * * * * * * * */
int fmcadc_zio_stop_acquisition(struct fmcadc_dev *dev,
unsigned int flags);
struct fmcadc_dev *fmcadc_zio_open(const struct fmcadc_board_type *dev,
unsigned int dev_id,
unsigned int details)
struct fmcadc_dev *fmcadc_zio_open(const struct fmcadc_board_type *b,
unsigned int dev_id,
unsigned long totalsamples,
unsigned int nbuffer,
unsigned long flags)
{
struct __fmcadc_dev_zio *fa = NULL;
struct __fmcadc_dev_zio *fa;
struct stat st;
char *syspath, *devpath, fname[128];
int udev_zio_dir = 1;
if (strlen(dev->devname) > 12) {
fprintf(stderr,
"%s: name \"%s\" is too long. ZIO's name are 12byte\n",
__func__, dev->devname);
return NULL ;
}
/* Check if device exists by looking in sysfs */
asprintf(&syspath, "%s/%s-%04x", ZIO_SYS_PATH, b->devname, dev_id);
if (stat(syspath, &st))
goto out_fa_stat; /* ENOENT or equivalent */
/* check if device exists by looking in ZIO sysfs */
asprintf(&syspath, "%s/%s-%04x", ZIO_SYS_PATH, dev->devname, dev_id);
if (stat(syspath, &st)) {
goto out_fa_stat;
}
/* Check where are ZIO char devices x*/
if (stat(ZIO_DEV_PATH, &st)) {
/*
* ZIO driver are not in /dev/zio, but in /dev with all other
* drivers
*/
/* ZIO char devices are in /dev/zio or just /dev (older udev) */
if (stat("/dev/zio", &st) < 0)
udev_zio_dir = 0;
}
asprintf(&devpath, "%s/%s-%04x", (udev_zio_dir ? ZIO_DEV_PATH : "/dev"),
dev->devname, dev_id);
/* Path exists, so device is there */
asprintf(&devpath, "%s/%s-%04x", (udev_zio_dir ? "/dev/zio" : "/dev"),
b->devname, dev_id);
/* Sysfs path exists, so device is there, hopefully */
fa = calloc(1, sizeof(*fa));
if (!fa) {
if (!fa)
goto out_fa_alloc;
}
fa->sysbase = syspath;
fa->devbase = devpath;
fa->cset = details;
fa->cset = 0;
/* Open char devices */
sprintf(fname, "%s-0-i-ctrl", fa->devbase);
......@@ -84,9 +66,16 @@ struct fmcadc_dev *fmcadc_zio_open(const struct fmcadc_board_type *dev,
sprintf(fname, "%s-0-i-data", fa->devbase);
fa->fdd = open(fname, O_RDONLY);
if (fa->fdc < 0 || fa->fdd < 0)
goto out_fa_open;
goto out_fa_open;
fa->gid.board = b;
fa->gid.board = dev;
/*
* We need to save the page size and samplesize.
* Samplesize includes the nchan in the count.
*/
fa->samplesize = 8; /* FIXME: should read sysfs instead -- where? */
fa->pagesize = getpagesize();
/* Finally, support verbose operation */
if (getenv("LIB_FMCADC_VERBOSE"))
......@@ -95,64 +84,39 @@ struct fmcadc_dev *fmcadc_zio_open(const struct fmcadc_board_type *dev,
return (void *) &fa->gid;
out_fa_open:
if (fa->fdc >= 0)
close(fa->fdc);
if (fa->fdd >= 0)
close(fa->fdd);
free(fa);
out_fa_alloc:
free(devpath);
out_fa_stat:
free(syspath);
return NULL ;
return NULL;
}
struct fmcadc_dev *fmcadc_zio_open_by_lun(char *name, int lun)
{
/* TODO implement*/
return NULL ;
}
int fmcadc_zio_close(struct fmcadc_dev *dev)
{
struct __fmcadc_dev_zio *fa = to_dev_zio(dev);
/* If char device are open, close it */
if (fa->fdc >= 0)
close(fa->fdc);
fa->fdc = -1;
if (fa->fdd >= 0)
close(fa->fdd);
fa->fdd = -1;
/* Stop active acquisition */
fmcadc_zio_stop_acquisition(dev, 0);
close(fa->fdc);
close(fa->fdd);
free(fa->sysbase);
free(fa->devbase);
free(fa);
return 0;
}
/* Handle acquisition */
int fmcadc_zio_start_acquisition(struct fmcadc_dev *dev,
unsigned int flags, struct timeval *timeout)
/* poll is used by start, so it's defined first */
int fmcadc_zio_acq_poll(struct fmcadc_dev *dev,
unsigned int flags, struct timeval *timeout)
{
struct __fmcadc_dev_zio *fa = to_dev_zio(dev);
uint32_t cmd;
fd_set set;
int err;
if (fa->fdc < 0) {
errno = EIO;
return -1;
}
cmd = 1;
err = fa_zio_sysfs_set(fa, "cset0/fsm-command", &cmd);
if (err) {
/*
* It returns error when we cannot start
*/
return err;
}
/* So, first sample and blocking read. Wait.. */
FD_ZERO(&set);
FD_SET(fa->fdc, &set);
err = select(fa->fdc + 1, &set, NULL, NULL, timeout);
......@@ -166,11 +130,28 @@ int fmcadc_zio_start_acquisition(struct fmcadc_dev *dev,
return err;
}
}
int fmcadc_zio_stop_acquisition(struct fmcadc_dev *dev,
unsigned int flags)
int fmcadc_zio_acq_start(struct fmcadc_dev *dev,
unsigned int flags, struct timeval *timeout)
{
struct __fmcadc_dev_zio *fa = to_dev_zio(dev);
uint32_t cmd = 1; /* hw command for "start" */
int err;
err = fa_zio_sysfs_set(fa, "cset0/fsm-command", &cmd);
if (err)
return err;
if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0)
return 0;
return fmcadc_zio_acq_poll(dev, flags, timeout);
}
int fmcadc_zio_acq_stop(struct fmcadc_dev *dev, unsigned int flags)
{
struct __fmcadc_dev_zio *fa = to_dev_zio(dev);
uint32_t cmd = 2;
uint32_t cmd = 2; /* hw command for "stop" */
return fa_zio_sysfs_set(fa, "cset0/fsm-command", &cmd);
}
......
......@@ -8,85 +8,41 @@
/*
* offsetof and container_of come from kernel.h header file
*/
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = ((void *)ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#define to_dev_zio(dev) (container_of(dev, struct __fmcadc_dev_zio, gid))
/* ->open takes different args than open(), so fa a fun to use tpyeof */
struct fmcadc_board_type;
struct fmcadc_dev *fmcadc_internal_open(const struct fmcadc_board_type *b,
unsigned int dev_id,
unsigned long totalsamples,
unsigned int nbuffer,
unsigned long flags);
/*
* fmcadc_op: it describes the set of operation that a device library should
* support
*
* @start_acquisition start the acquisition
* @dev: device where to start acquiring
* @flags:
* @timeout: it can be used to specify how much time wait that acquisition
* is over. This value follow the select() policy: NULL to wait
* until acquisition is over; {0, 0} to return immediately
* without wait; {x, y} to wait acquisition end for a specified
* time
*
* @stop_acquisition stop the acquisition
* @dev: device where to stop acquisition
* @flags:
*
* @apply_config specific operation to apply a configuration to the device
* @dev: device to configure
* @flags:
* @conf: configuration to apply on device.
*
* @retrieve_config specific board operation to get the current configuration
* of the device
* @dev: device where retireve configuration
* @flags:
* @conf: configuration to retrieve. The mask tell which value acquire,
* then the library will acquire and set the value in the "value"
* array
*
* @request_buffer get from the device a buffer
* @dev: device where look for a buffer
* @buf: where store the buffer. The user must allocate this structure.
* @flags:
* @timeout: it can be used to specify how much time wait that a buffer is
* ready. This value follow the select() policy: NULL to wait
* until acquisition is over; {0, 0} to return immediately
* without wait; {x, y} to wait acquisition end for a specified
* time
*
* @release_buffer release the resources of a given buffer
* @dev: device that generate the buffer
* @buf: buffer to release
* The operations structure is the device-specific backend of the library
*/
struct fmcadc_op {
/* Handle board */
struct fmcadc_dev *(*open)(const struct fmcadc_board_type *dev,
unsigned int dev_id,
unsigned int details);
struct fmcadc_dev *(*open_by_lun)(char *devname, int lun);
int (*close)(struct fmcadc_dev *dev);
/* Handle acquisition */
int (*start_acquisition)(struct fmcadc_dev *dev,
unsigned int flags,
struct timeval *timeout);
int (*stop_acquisition)(struct fmcadc_dev *dev,
unsigned int flags);
/* Handle configuration */
int (*apply_config)(struct fmcadc_dev *dev,
unsigned int flags,
struct fmcadc_conf *conf);
int (*retrieve_config)(struct fmcadc_dev *dev,
struct fmcadc_conf *conf);
/* Handle buffers */
struct fmcadc_buffer *(*request_buffer)(struct fmcadc_dev *dev,
int nsamples,
void *(*alloc_fn)(size_t),
unsigned int flags,
struct timeval *timeout);
int (*release_buffer)(struct fmcadc_dev *dev,
struct fmcadc_buffer *buf,
void (*free_fn)(void *));
char *(*strerror)(int errnum);
struct fmcadc_operations {
typeof(fmcadc_internal_open) *open;
typeof(fmcadc_close) *close;
typeof(fmcadc_acq_start) *acq_start;
typeof(fmcadc_acq_poll) *acq_poll;
typeof(fmcadc_acq_stop) *acq_stop;
typeof(fmcadc_apply_config) *apply_config;
typeof(fmcadc_retrieve_config) *retrieve_config;
typeof(fmcadc_get_param) *get_param;
typeof(fmcadc_set_param) *set_param;
typeof(fmcadc_request_buffer) *request_buffer;
typeof(fmcadc_fill_buffer) *fill_buffer;
typeof(fmcadc_tstamp_buffer) *tstamp_buffer;
typeof(fmcadc_release_buffer) *release_buffer;
};
/*
* This structure describes the board supported by the library
......@@ -98,11 +54,11 @@ struct fmcadc_op {
* @fa_op pointer to a set of operations
*/
struct fmcadc_board_type {
char *name;
char *devname;
char *driver_type;
uint32_t capabilities[__FMCADC_CONF_TYPE_LAST_INDEX];
struct fmcadc_op *fa_op;
char *name;
char *devname;
char *driver_type;
uint32_t capabilities[__FMCADC_CONF_TYPE_LAST_INDEX];
struct fmcadc_operations *fa_op;
};
/*
......@@ -124,28 +80,39 @@ struct __fmcadc_dev_zio {
unsigned long flags;
char *devbase;
char *sysbase;
unsigned long samplesize;
unsigned long pagesize;
/* Mandatory field */
struct fmcadc_gid gid;
};
#define FMCADC_FLAG_VERBOSE 0x00000001
#define FMCADC_FLAG_MALLOC 0x00000002 /* allocate data */
#define FMCADC_FLAG_MMAP 0x00000004 /* mmap data */
/* Open and close live in board.c, as open scans the list of boards */
struct fmcadc_dev *fmcadc_zio_open(const struct fmcadc_board_type *dev,
/* The board-specific functions are defined in fmc-adc-100m14b4cha.c */
struct fmcadc_dev *fmcadc_zio_open(const struct fmcadc_board_type *b,
unsigned int dev_id,
unsigned int details);
struct fmcadc_dev *fmcadc_zio_open_by_lun(char *name, int lun);
unsigned long totalsamples,
unsigned int nbuffer,
unsigned long flags);
int fmcadc_zio_close(struct fmcadc_dev *dev);
/* The board-specific functions are defined in fmc-adc-100m14b4cha.c */
int fmcadc_zio_start_acquisition(struct fmcadc_dev *dev,
unsigned int flags, struct timeval *timeout);
int fmcadc_zio_stop_acquisition(struct fmcadc_dev *dev,
unsigned int flags);
int fmcadc_zio_acq_start(struct fmcadc_dev *dev,
unsigned int flags, struct timeval *timeout);
int fmcadc_zio_acq_poll(struct fmcadc_dev *dev, unsigned int flags,
struct timeval *timeout);
int fmcadc_zio_acq_stop(struct fmcadc_dev *dev,
unsigned int flags);
struct fmcadc_buffer *fmcadc_zio_request_buffer(struct fmcadc_dev *dev,
int nsamples,
void *(*alloc)(size_t),
unsigned int flags,
struct timeval *timeout);
unsigned int flags);
int fmcadc_zio_fill_buffer(struct fmcadc_dev *dev,
struct fmcadc_buffer *buf,
unsigned int flags,
struct timeval *timeout);
struct fmcadc_timestamp *fmcadc_zio_tstamp_buffer(struct fmcadc_buffer *buf,
struct fmcadc_timestamp *);
int fmcadc_zio_release_buffer(struct fmcadc_dev *dev,
struct fmcadc_buffer *buf,
void (*free_fn)(void *));
......@@ -155,6 +122,12 @@ int fmcadc_zio_apply_config(struct fmcadc_dev *dev, unsigned int flags,
struct fmcadc_conf *conf);
int fmcadc_zio_retrieve_config(struct fmcadc_dev *dev,
struct fmcadc_conf *conf);
int fmcadc_zio_set_param(struct fmcadc_dev *dev, char *name,
char *sptr, int *iptr);
int fmcadc_zio_get_param(struct fmcadc_dev *dev, char *name,
char *sptr, int *iptr);
int fa_zio_sysfs_set(struct __fmcadc_dev_zio *fa, char *name,
uint32_t *value);
......
......@@ -26,16 +26,15 @@ enum fmcadc_supported_board {
__FMCADC_SUPPORTED_BOARDS_LAST_INDEX,
};
extern const struct fmcadc_board_type
*fmcadc_board_types[__FMCADC_SUPPORTED_BOARDS_LAST_INDEX];
/* The buffer hosts data and metadata, plus informative fields */
struct fmcadc_buffer {
void *data;
void *metadata;
int samplesize;
int nsamples;
char *drivername;
struct fmcadc_dev *dev;
void *mapaddr;
unsigned long maplen;
unsigned long flags; /* internal to the library */
};
......@@ -133,11 +132,11 @@ extern void fmcadc_exit(void);
extern char *fmcadc_strerror(int errnum);
extern struct fmcadc_dev *fmcadc_open(char *name, unsigned int dev_id,
unsigned long totalsize,
unsigned long totalsamples,
unsigned int nbuffer,
unsigned long flags);
extern struct fmcadc_dev *fmcadc_open_by_lun(char *name, int lun,
unsigned long totalsize,
unsigned long totalsamples,
unsigned int nbuffer,
unsigned long flags);
extern int fmcadc_close(struct fmcadc_dev *dev);
......@@ -154,15 +153,19 @@ extern int fmcadc_apply_config(struct fmcadc_dev *dev, unsigned int flags,
struct fmcadc_conf *conf);
extern int fmcadc_retrieve_config(struct fmcadc_dev *dev,
struct fmcadc_conf *conf);
extern int fmcadc_get_param(struct fmcadc_dev *dev, char *name,
char *sptr, int *iptr);
extern int fmcadc_set_param(struct fmcadc_dev *dev, char *name,
char *sptr, int *iptr);
extern struct fmcadc_buffer *fmcadc_request_buffer(struct fmcadc_dev *dev,
int nsamples,
void *(*alloc_fn)(size_t),
unsigned int flags,
struct timeval *timeout);
unsigned int flags);
extern int fmcadc_fill_buffer(struct fmcadc_dev *dev,
struct fmcadc_buffer *buf,
unsigned int flags);
unsigned int flags,
struct timeval *timeout);
extern struct fmcadc_timestamp *fmcadc_tstamp_buffer(struct fmcadc_buffer *buf,
struct fmcadc_timestamp *);
extern int fmcadc_release_buffer(struct fmcadc_dev *dev,
......
/*
* Copyright CERN 2013, GNU GPL 2 or later.
* Author: Alessandro Rubini
*/
#include "fmcadc-lib.h"
/* We currently do nothing in init/exit. We might check /proc/meminfo... */
int fmcadc_init(void)
{
return 0;
}
void fmcadc_exit(void)
{
return;
}
......@@ -21,205 +21,129 @@
#include "fmcadc-lib.h"
#include "fmcadc-lib-int.h"
const struct fmcadc_board_type
*fmcadc_board_types[__FMCADC_SUPPORTED_BOARDS_LAST_INDEX] = {
&fmcadc_100ms_4ch_14bit,
};
/* * * * * * * * * * * * * * * * * Handle Device * * * * * * * * * * * * * */
/* * * * * * * * * * * * * * * Handle Acquisition * * * * * * * * * * * * * */
/*
* fmcadc_acq_start
* @dev: device where to start acquiring
* @flags:
* @timeout: it can be used to specify how much time wait that acquisition is
* over. This value follow the select() policy: NULL to wait until
* acquisition is over; {0, 0} to return immediately without wait;
* {x, y} to wait acquisition end for a specified time
*/
int fmcadc_acq_start(struct fmcadc_dev *dev,
unsigned int flags,
struct timeval *timeout)
{
struct fmcadc_gid *b = (void *)dev;
struct fmcadc_gid *g = (void *)dev;
const struct fmcadc_board_type *b = g->board;
if (!dev) {
/* dev cannot be NULL */
errno = EINVAL;
return -1;
}
return b->fa_op->acq_start(dev, flags, timeout);
}
if (b->board->fa_op->start_acquisition) {
return b->board->fa_op->start_acquisition(dev, flags, timeout);
} else {
errno = FMCADC_ENOP;
return -1;
}
int fmcadc_acq_poll(struct fmcadc_dev *dev, unsigned int flags,
struct timeval *timeout)
{
struct fmcadc_gid *g = (void *)dev;
const struct fmcadc_board_type *b = g->board;
return b->fa_op->acq_poll(dev, flags, timeout);
}
/*
* fmcadc_acq_stop
* @dev: device where to stop acquisition
* @flags:
*/
int fmcadc_acq_stop(struct fmcadc_dev *dev, unsigned int flags)
{
struct fmcadc_gid *b = (void *)dev;
struct fmcadc_gid *g = (void *)dev;
const struct fmcadc_board_type *b = g->board;
if (!dev) {
/* dev cannot be NULL */
errno = EINVAL;
return -1;
}
if (b->board->fa_op->stop_acquisition) {
return b->board->fa_op->stop_acquisition(dev, flags);
} else {
errno = FMCADC_ENOP;
return -1;
}
return b->fa_op->acq_stop(dev, flags);
}
/* * * * * * * * * * * * * * Handle Configuration * * * * * * * * * * * * * */
/*
* fmcadc_apply_config
* @dev: device to configure
* @flags:
* @conf: configuration to apply on device.
*/
int fmcadc_apply_config(struct fmcadc_dev *dev, unsigned int flags,
struct fmcadc_conf *conf)
{
struct fmcadc_gid *b = (void *)dev;
struct fmcadc_gid *g = (void *)dev;
const struct fmcadc_board_type *b = g->board;
uint64_t cap_mask;
if (!conf || !dev) {
/* conf and dev cannot be NULL*/
errno = EINVAL;
return -1;
}
if (!conf->mask) {
errno = FMCADC_ENOMASK;
return -1; /* Nothing to do */
}
cap_mask = b->board->capabilities[conf->type];
cap_mask = b->capabilities[conf->type];
if ((cap_mask & conf->mask) != conf->mask) {
/* Unsupported capabilities */
fprintf(stderr, "Apply Config, wrong mask 0x%llx (0x%llx)",
conf->mask, cap_mask);
errno = FMCADC_ENOCAP;
return -1;
}
if (b->board->fa_op->apply_config) {
/* Apply config */
return b->board->fa_op->apply_config(dev, flags, conf);
} else {
/* Unsupported */
errno = FMCADC_ENOP;
return -1;
}
return b->fa_op->apply_config(dev, flags, conf);
}
/*
* fmcadc_retrieve_config
* @dev: device where retireve configuration
* @flags:
* @conf: configuration to retrieve. The mask tell which value acquire, then
* the library will acquire and set the value in the "value" array
*/
int fmcadc_retrieve_config(struct fmcadc_dev *dev, struct fmcadc_conf *conf)
{
struct fmcadc_gid *b = (void *)dev;
struct fmcadc_gid *g = (void *)dev;
const struct fmcadc_board_type *b = g->board;
uint64_t cap_mask;
if (!conf || !dev) {
/* conf and dev cannot be NULL*/
errno = EINVAL;
return -1;
}
if (!conf->mask) {
errno = FMCADC_ENOMASK;
return -1; /* Nothing to do */
}
cap_mask = b->board->capabilities[conf->type];
cap_mask = b->capabilities[conf->type];
if ((cap_mask & conf->mask) != conf->mask) {
/* Unsupported capabilities */
fprintf(stderr, "Apply Config, wrong mask 0x%llx (0x%llx)",
conf->mask, cap_mask);
errno = FMCADC_ENOCAP;
return -1;
}
return b->fa_op->retrieve_config(dev, conf);
}
if (b->board->fa_op->retrieve_config) {
/* Apply config */
return b->board->fa_op->retrieve_config(dev, conf);
} else {
/* Unsupported */
errno = FMCADC_ENOP;
return -1;
}
int fmcadc_get_param(struct fmcadc_dev *dev, char *name,
char *sptr, int *iptr)
{
struct fmcadc_gid *g = (void *)dev;
const struct fmcadc_board_type *b = g->board;
return b->fa_op->get_param(dev, name, sptr, iptr);
}
int fmcadc_set_param(struct fmcadc_dev *dev, char *name,
char *sptr, int *iptr)
{
struct fmcadc_gid *g = (void *)dev;
const struct fmcadc_board_type *b = g->board;
return b->fa_op->set_param(dev, name, sptr, iptr);
}
/*
* fmcadc_request_buffer
* @dev: device where look for a buffer
* @nsamples: size of this buffer
* @alloc: user-defined allocator
* @flags:
* @timeout: it can be used to specify how much time wait that a buffer is
* ready. This value follow the select() policy: NULL to wait until
* acquisition is over; {0, 0} to return immediately without wait;
* {x, y} to wait acquisition end for a specified time
*/
struct fmcadc_buffer *fmcadc_request_buffer(struct fmcadc_dev *dev,
int nsamples,
void *(*alloc)(size_t),
unsigned int flags,
struct timeval *timeout)
unsigned int flags)
{
struct fmcadc_gid *b = (void *)dev;
struct fmcadc_gid *g = (void *)dev;
const struct fmcadc_board_type *b = g->board;
if (!dev) {
/* dev cannot be NULL */
errno = EINVAL;
return NULL;
}
return b->fa_op->request_buffer(dev, nsamples, alloc, flags);
}
if (b->board->fa_op->request_buffer) {
return b->board->fa_op->request_buffer(dev, nsamples,
alloc, flags, timeout);
} else {
/* Unsupported */
errno = FMCADC_ENOP;
return NULL;
}
int fmcadc_fill_buffer(struct fmcadc_dev *dev,
struct fmcadc_buffer *buf,
unsigned int flags,
struct timeval *timeout)
{
struct fmcadc_gid *g = (void *)dev;
const struct fmcadc_board_type *b = g->board;
return b->fa_op->fill_buffer(dev, buf, flags, timeout);
}
struct fmcadc_timestamp *fmcadc_tstamp_buffer(struct fmcadc_buffer *buf,
struct fmcadc_timestamp *ts)
{
struct fmcadc_gid *g = (void *)buf->dev;
const struct fmcadc_board_type *b = g->board;
return b->fa_op->tstamp_buffer(buf, ts);
}
/*
* fmcadc_release_buffer
* @dev: device that generate the buffer
* @buf: buffer to release
*/
int fmcadc_release_buffer(struct fmcadc_dev *dev, struct fmcadc_buffer *buf,
void (*free)(void *))
{
struct fmcadc_gid *b = (void *)dev;
struct fmcadc_gid *g = (void *)dev;
const struct fmcadc_board_type *b = g->board;
if (!dev || !buf) {
/* dev and buf cannot be NULL */
errno = EINVAL;
return -1;
}
if (!buf)
return 0;
if (b->board->fa_op->release_buffer) {
return b->board->fa_op->release_buffer(dev, buf, free);
} else {
/* Unsupported */
errno = FMCADC_ENOP;
return -1;
}
return b->fa_op->release_buffer(dev, buf, free);
}
......@@ -6,6 +6,7 @@ LDFLAGS = -static -L$(LIBADC) -lfmcadc
DEMOS := fald-simple-acq
DEMOS += fald-simple-get-conf
DEMOS += fald-test
all: demo
......
......@@ -29,6 +29,7 @@ static void fald_help()
printf(" --under-sample|-U <num> pick 1 sample every <num>\n");
printf(" --threshold|-t <num> internal trigger threshold\n");
printf(" --channel|-c <num> internal channel to use as trigger (0..3)\n");
printf(" --tiemout|-T <millisec> timeout for acquisition\n");
printf(" --negative-edge internal trigger is falling edge\n");
printf(" --binary|-B <file> save binary to <file>\n");
printf(" --multi-binary|-M <file> save two files per shot: <file>.0000.ctrl etc\n");
......@@ -47,6 +48,7 @@ static struct option options[] = {
{"under-sample",required_argument, 0, 'u'},
{"threshold", required_argument, 0, 't'},
{"channel", required_argument, 0, 'c'},
{"timeout", required_argument, 0, 'T'},
{"negative-edge", no_argument, &trgval[FMCADC_CONF_TRG_POLARITY], 1},
/* new options, to help stress-test */
......@@ -63,7 +65,7 @@ static struct option options[] = {
{0, 0, 0, 0}
};
#define GETOPT_STRING "b:a:n:d:u:t:c:B:M:Np:P:D:h"
#define GETOPT_STRING "b:a:n:d:u:t:c:T:B:M:Np:P:D:h"
int main(int argc, char *argv[])
{
......@@ -72,6 +74,7 @@ int main(int argc, char *argv[])
struct fmcadc_conf trg, acq;
int i, c, err, opt_index, binmode = 0;
int nshots = 1, presamples = 0, postsamples = 16;
int timeout = -1;
unsigned int dev_id = 0;
char *basefile = NULL;
char fname[PATH_MAX];
......@@ -129,6 +132,9 @@ int main(int argc, char *argv[])
fmcadc_set_conf(&trg, FMCADC_CONF_TRG_SOURCE_CHAN,
atoi(optarg));
break;
case 'T':
timeout = atoi(optarg);
break;
case 'B':
binmode = 1; /* do binary (default is 0) */
basefile = optarg;
......@@ -158,7 +164,7 @@ int main(int argc, char *argv[])
}
/* Open the ADC */
adc = fmcadc_open("fmcadc_100MS_4ch_14bit", dev_id,
adc = fmcadc_open("fmc-adc-100m14b4cha", dev_id,
nshots * (presamples + postsamples),
nshots,
0);
......@@ -210,15 +216,43 @@ int main(int argc, char *argv[])
exit(1);
}
/* Start acquisition and wait until it completes */
err = fmcadc_acq_start(adc, 0 , NULL);
if (timeout < 0) {
/* Start acquisition and wait until it completes */
err = fmcadc_acq_start(adc, 0 , NULL);
} else {
/* Start acquisition and don't wait. We use acq_poll() later */
struct timeval tv = {0, 0};
err = fmcadc_acq_start(adc, 0 , &tv);
}
if (err) {
fprintf(stderr, "%s: cannot start acquisition: %s\n",
argv[0], fmcadc_strerror(errno));
exit(1);
}
/* Retrieve buffer for each shot */
/* Now, if a timeout was specified, use the poll method */
if (timeout >= 0) {
struct timeval tv = {timeout / 1000, timeout % 1000};
err = fmcadc_acq_poll(adc, 0 , &tv);
}
if (err) {
fprintf(stderr, "%s: timeout after %i ms: %s\n", argv[0],
timeout, strerror(errno));
exit(1);
}
/* Allocate a buffer in the default way */
buf = fmcadc_request_buffer(adc, presamples + postsamples,
NULL /* alloc */, 0);
if (!buf) {
fprintf(stderr, "Cannot allocate buffer (%s)\n",
fmcadc_strerror(errno));
exit(1);
}
/* Fill the buffer once for each shot */
for (i = 0; i < acq.value[FMCADC_CONF_ACQ_N_SHOTS]; ++i) {
struct zio_control *ctrl;
int j, ch;
......@@ -227,16 +261,12 @@ int main(int argc, char *argv[])
if (binmode < 0) /* no data must be acquired */
break;
/* Currently this request_buffer() actually reads data */
buf = fmcadc_request_buffer(adc,
presamples + postsamples,
NULL /* alloc */,
0, NULL /* timeout */);
if (!buf) {
fprintf(stderr, "%s: shot %i/%i: cannot get a buffer:"
err = fmcadc_fill_buffer(adc, buf, 0, NULL);
if (err) {
fprintf(stderr, "%s: shot %i/%i: cannot fill buffer:"
" %s\n", argv[0], i + i,
acq.value[FMCADC_CONF_ACQ_N_SHOTS],
fmcadc_strerror(errno));
fmcadc_strerror(errno));
exit(1);
}
ctrl = buf->metadata;
......@@ -301,11 +331,11 @@ int main(int argc, char *argv[])
printf("%7i", *(data++));
printf("\n");
}
fmcadc_release_buffer(adc, buf, NULL);
}
if (binmode == 1)
fclose(f);
fmcadc_release_buffer(adc, buf, NULL);
fmcadc_close(adc);
exit(0);
}
/* Copyright 2013 CERN
* Author: Federico Vaga <federico.vaga@gmail.com>
* License: GPLv2
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <getopt.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
#include <errno.h>
#include <linux/zio-user.h>
#include <fmcadc-lib.h>
/* Global configuration*/
#define N_CHAN 4
#define CARD_NAME "fmc-adc-100m14b4cha"
unsigned nshots = 1;
unsigned presamples = 10;
unsigned postsamples = 10;
int print_data = 1;
void print_buffer_content(struct fmcadc_buffer * buf);
int read_with_one_buffer(struct fmcadc_dev *dev);
int read_with_n_buffer(struct fmcadc_dev *dev);
int main(int argc, char *argv[])
{
unsigned long totalsamples = nshots * (presamples + postsamples);
unsigned int dev_id, n_buffers = nshots;
struct fmcadc_conf acq, trg;
struct fmcadc_dev *dev;
int test = 0, err;
if (argc != 3) {
fprintf(stderr, "%s: Use \"%s <dev_id> <testnr>\n",
argv[0], argv[1]);
exit(1);
}
sscanf(argv[1], "%x", &dev_id);
sscanf(argv[2], "%i", &test);
/* Change parameters from environment */
if (getenv("FALD_TEST_NSHOTS"))
nshots = atoi(getenv("FALD_TEST_NSHOTS"));
if (getenv("FALD_TEST_PRE_S"))
presamples = atoi(getenv("FALD_TEST_PRE_S"));
if (getenv("FALD_TEST_POST_S"))
postsamples = atoi(getenv("FALD_TEST_POST_S"));
if (getenv("FALD_TEST_NOPRINT"))
print_data = 0;
fmcadc_init();
dev = fmcadc_open(CARD_NAME, dev_id, totalsamples, n_buffers, 0);
if (!dev) {
fprintf(stderr, "%s: fmcadc_open(%s, 0x%x): %s\n", argv[0],
CARD_NAME, dev_id, strerror(errno));
exit(1);
}
if (strcmp(fmcadc_get_driver_type(dev), "zio")) {
fprintf(stderr, "%s: not a zio driver, aborting\n", argv[0]);
exit(1);
}
if (0) { /* We can't change buffer, because chardevs are open */
err = fmcadc_set_param(dev, "cset0/current_buffer", argv[3], 0);
if (err) {
fprintf(stderr, "%s: cannot set '%s' as buffer: %s\n",
argv[0], argv[3], fmcadc_strerror(errno));
exit(1);
}
}
/* FIXME: use nshots to set cset0/chani/buffer/max-buffer-len */
/* FIXME: use maxsize to set cset0/chani/buffer/max-buffer-kb */
/* configure acquisition parameters */
memset(&acq, 0, sizeof(acq));
acq.type = FMCADC_CONF_TYPE_ACQ;
fmcadc_set_conf(&acq, FMCADC_CONF_ACQ_N_SHOTS, nshots);
fmcadc_set_conf(&acq, FMCADC_CONF_ACQ_PRE_SAMP, presamples);
fmcadc_set_conf(&acq, FMCADC_CONF_ACQ_POST_SAMP, postsamples);
err = fmcadc_apply_config(dev, 0, &acq);
if (err) {
fprintf(stderr, "%s: apply_config(acq): %s\n",
argv[0], fmcadc_strerror(errno));
exit(1);
}
/* configure trigger parameters */
memset(&trg, 0, sizeof(trg));
trg.type = FMCADC_CONF_TYPE_TRG;
fmcadc_set_conf(&trg, FMCADC_CONF_TRG_SOURCE, 1); /* external */
err = fmcadc_apply_config(dev, 0, &trg);
if (err) {
fprintf(stderr, "%s: apply_config(trigger): %s\n",
argv[0], fmcadc_strerror(errno));
exit(1);
}
/* Start the acquisition */
err = fmcadc_acq_start(dev, 0 , NULL);
if (err) {
fprintf(stderr, "%s: cannot start acquisition: %s\n",
argv[0], fmcadc_strerror(errno));
exit(1);
}
switch(test) {
case 0:
err = read_with_one_buffer(dev);
break;
case 1:
err = read_with_n_buffer(dev);
break;
}
if (err) {
fprintf(stderr, "%s: problem with read buffer: %s\n",
argv[0], fmcadc_strerror(errno));
exit(1);
}
/* Stop acquisition, close device and exit from library */
fmcadc_acq_stop(dev, 0);
fmcadc_close(dev);
fmcadc_exit();
exit(0);
}
/* Read all shots with a single buffer and then release it */
int read_with_one_buffer(struct fmcadc_dev *dev)
{
struct fmcadc_buffer *buf;
int err = 0, i;
buf = fmcadc_request_buffer(dev, presamples + postsamples, NULL, 0);
for (i = 0; i < nshots; ++i) {
err = fmcadc_fill_buffer(dev, buf, 0, NULL);
if (err)
break;
print_buffer_content(buf);
}
fmcadc_release_buffer(dev, buf, NULL);
return err;
}
/* Read all shots with a multiple buffer and then release them
* request, fill and release are separated in different loop to show that
* are not strictly consecutive operations
*/
int read_with_n_buffer(struct fmcadc_dev *dev)
{
struct fmcadc_buffer *buf[nshots] ;
int err = 0, i;
/* Allocate all buffers before the use */
for (i = 0; i < nshots; ++i)
buf[i] = fmcadc_request_buffer(dev,
presamples + postsamples, NULL, 0);
/* Fill all buffers */
for (i = 0; i < nshots; ++i) {
err = fmcadc_fill_buffer(dev, buf[i], 0, NULL);
if (err)
break;
print_buffer_content(buf[i]);
}
/* Release all buffers */
for (i = 0; i < nshots; ++i)
fmcadc_release_buffer(dev, buf[i], NULL);
return err;
}
void print_buffer_content(struct fmcadc_buffer * buf)
{
int16_t *data = buf->data; /* get data */
struct fmcadc_timestamp *ts;
int i, ch;
ts = fmcadc_tstamp_buffer(buf, NULL);
printf("timestamp %lli:%lli:%lli\n",
(long long)ts->secs,
(long long)ts->ticks,
(long long)ts->bins);
if (!print_data)
return;
for (i = 0; i < presamples + postsamples; i++) {
printf("%5i ", i - presamples);
for (ch = 0; ch < N_CHAN; ch++)
printf("%7i", *(data++));
printf("\n");
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment