Commit d2368a4c authored by Alessandro Rubini's avatar Alessandro Rubini

lib: implement buffers properly, add param commands too

Signed-off-by: Alessandro Rubini's avatarAlessandro Rubini <rubini@gnudd.com>
parent eb9273ee
......@@ -42,6 +42,9 @@ struct fmcadc_operations fa_100ms_4ch_14bit_op = {
.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,
......
......@@ -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,68 +25,91 @@
#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),
......@@ -93,45 +117,51 @@ struct fmcadc_buffer *fmcadc_zio_request_buffer(struct fmcadc_dev *dev,
{
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;
/* So, first sample and blocking read. Wait.. */
FD_ZERO(&set);
FD_SET(fa->fdc, &set);
err = select(fa->fdc + 1, &set, NULL, NULL, NULL);
if (err == 0) {
errno = EAGAIN;
return NULL; /* no free, but the function is wrong generally */
} else if (err < 0) {
}
buf->metadata = calloc(1, sizeof(struct zio_control));
if (!buf->metadata) {
free(buf);
errno = ENOMEM;
return NULL;
}
/* 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;
/* 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;
}
buf->data = data;
buf->metadata = (void *) ctrl;
/* Copy other information */
buf->samplesize = fa->samplesize;
buf->nsamples = nsamples;
buf->dev = (void *)&fa->gid;
buf->flags = flags;
return buf;
out_data:
fmcadc_zio_release_ctrl(ctrl);
out_ctrl:
return NULL;
}
int fmcadc_zio_fill_buffer(struct fmcadc_dev *dev,
......@@ -139,21 +169,55 @@ int fmcadc_zio_fill_buffer(struct fmcadc_dev *dev,
unsigned int flags,
struct timeval *timeout)
{
return -1;
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);
ret = select(fa->fdc + 1, &set, NULL, NULL, timeout);
if (ret == 0) {
errno = EAGAIN;
return -1;
} else if (ret < 0) {
return -1;
}
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;
}
struct fmcadc_timestamp *fmcadc_zio_tstamp_buffer(struct fmcadc_buffer *buf,
struct fmcadc_timestamp *ts)
{
return NULL;
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;
}
......@@ -25,82 +25,136 @@
#define FMCADC_CONF_GET 0
#define FMCADC_CONF_SET 1
static int __fa_zio_sysfs_get(char *path, uint32_t *resp)
/*
* Internal functions to read and write a string.
* Trailing newlines are added/removed as needed
*/
static int __fa_zio_sysfs_set(struct __fmcadc_dev_zio *fa, char *name,
char *val, int maxlen)
{
FILE *f = fopen(path, "r");
int ret;
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 = EINVAL; /* in case it's a scanf-only error */
if (fscanf(f, "%i", resp) != 1)
ret = -1;
fclose(f);
return 0;
ret = write(fd, newval, len);
close(fd);
if (ret < 0)
return -1;
if (ret == len)
return 0;
errno = EIO; /* short write */
return -1;
}
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).
* Thhey mabage 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;
}
/*
* 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;
snprintf(pathname, sizeof(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 (%s)\n",
pathname, strerror(errno));
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 */
snprintf(pathname, sizeof(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 (%s)\n",
pathname, strerror(errno));
name, strerror(errno));
else
fprintf(stderr, "lib-fmcadc: %08x %5i -> %s\n",
(int)*value, (int)*value, pathname);
(int)*value, (int)*value, name);
return ret;
}
......
......@@ -70,6 +70,13 @@ struct fmcadc_dev *fmcadc_zio_open(const struct fmcadc_board_type *b,
fa->gid.board = b;
/*
* 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"))
fa->flags |= FMCADC_FLAG_VERBOSE;
......
......@@ -36,6 +36,8 @@ struct fmcadc_operations {
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;
......@@ -78,10 +80,14 @@ 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 */
/* The board-specific functions are defined in fmc-adc-100m14b4cha.c */
struct fmcadc_dev *fmcadc_zio_open(const struct fmcadc_board_type *b,
......@@ -116,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);
......
......@@ -153,6 +153,10 @@ 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,
......
......@@ -87,61 +87,63 @@ int fmcadc_retrieve_config(struct fmcadc_dev *dev, struct fmcadc_conf *conf)
return b->fa_op->retrieve_config(dev, conf);
}
/*
* 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
*/
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);
}
struct fmcadc_buffer *fmcadc_request_buffer(struct fmcadc_dev *dev,
int nsamples,
void *(*alloc)(size_t),
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);
} 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);
}
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