Commit 927e6e49 authored by Federico Vaga's avatar Federico Vaga

Merge branch 'hotfix/sdb_find_device'

parents d18cd552 a71418f3
......@@ -4,7 +4,7 @@ CURDIR:=$(shell /bin/pwd)
REPO_PARENT ?= $(CURDIR)/..
-include $(REPO_PARENT)/parent_common.mk
all: kernel tools lib libtools
all: kernel tools
FMC_BUS ?= fmc-bus
ZIO ?= zio
......@@ -24,11 +24,9 @@ export ZIO_ABS
export SVEC_SW_ABS
export VMEBUS_ABS
DIRS = $(FMC_BUS_ABS) $(ZIO_ABS) kernel tools lib libtools
DIRS = $(FMC_BUS_ABS) $(ZIO_ABS) kernel tools
kernel: $(FMC_BUS_ABS) $(ZIO_ABS)
lib: $(ZIO_ABS)
tools libtools: lib
.PHONY: all clean modules install modules_install $(DIRS)
.PHONY: gitmodules prereq_install prereq_install_warn
......
......@@ -15,3 +15,4 @@
*.tp
/*.txt
*.vr
_build
# Minimal makefile for Sphinx documentation
#
# Makefile for the documentation directory
#
# Copyright 1994,2000,2010,2011 Alessandro Rubini <rubini@linux.it>
#
#################
# There is not basenames here, all *.in are considered input
INPUT = $(wildcard *.in)
TEXI = $(INPUT:.in=.texi)
INFO = $(INPUT:.in=.info)
HTML = $(INPUT:.in=.html)
TXT = $(INPUT:.in=.txt)
PDF = $(INPUT:.in=.pdf)
ALL = $(INFO) $(HTML) $(TXT) $(PDF)
MAKEINFO ?= makeinfo
RELEASE=$(shell git describe --always --dirty)
%.texi: %.in
@rm -f $@
sed s/__RELEASE_GIT_ID__/$(RELEASE)/ $< | sed -f ./infofilter > $@
emacs -batch --no-site-file -l fixinfo $@
chmod -w $@
%.pdf: %.texi
texi2pdf --batch $<
%.info: %.texi
$(MAKEINFO) $< -o $@
%.html: %.texi
$(MAKEINFO) --html --no-split -o $@ $<
%.txt: %.texi
$(MAKEINFO) --no-headers $< > $@
##############################################
.PHONY: all images check terse clean install
.INTERMEDIATE: $(TEXI)
all: images $(ALL)
$(MAKE) terse
images::
if [ -d images ]; then $(MAKE) -C images || exit 1; fi
check: _err.ps
gs -sDEVICE=linux -r320x200x16 $<
terse:
for n in cp fn ky pg toc tp vr aux log; do rm -f *.$$n; done
rm -f *~
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXPROJ = FMCADC100M14bit4Channel
SOURCEDIR = .
BUILDDIR = _build
clean: terse
rm -f $(ALL) $(TEXI)
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
install:
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
\ No newline at end of file
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# FMC ADC 100M 14 bit 4 Channel documentation build configuration file, created by
# sphinx-quickstart on Thu Jan 25 09:54:50 2018.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx.ext.autodoc',
'sphinx.ext.todo',
'sphinx.ext.coverage',
'sphinx.ext.graphviz',]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'FMC ADC 100M 14 bit 4 Channel'
copyright = '2018, Federico Vaga <federico.vaga@cern.ch>'
author = 'Federico Vaga <federico.vaga@cern.ch>'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '5.0'
# The full version, including alpha/beta/rc tags.
release = '5.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
#html_theme = 'alabaster'
html_theme = "sphinx_rtd_theme"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# This is required for the alabaster theme
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
html_sidebars = {
'**': [
'relations.html', # needs 'show_related': True theme option to display
'searchbox.html',
]
}
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'FMCADC100M14bit4Channeldoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'FMCADC100M14bit4Channel.tex', 'FMC ADC 100M 14 bit 4 Channel Documentation',
'Federico Vaga \\textless{}federico.vaga@cern.ch\\textgreater{}', 'manual'),
]
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'fmcadc100m14bit4channel', 'FMC ADC 100M 14 bit 4 Channel Documentation',
[author], 1)
]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'FMCADC100M14bit4Channel', 'FMC ADC 100M 14 bit 4 Channel Documentation',
author, 'FMCADC100M14bit4Channel', 'One line description of project.',
'Miscellaneous'),
]
# -- Options for Epub output ----------------------------------------------
# Bibliographic Dublin Core info.
epub_title = project
epub_author = author
epub_publisher = author
epub_copyright = copyright
# The unique identifier of the text. This can be a ISBN number
# or the project homepage.
#
# epub_identifier = ''
# A unique identification for the text.
#
# epub_uid = ''
# A list of files that should not be packed into the epub file.
epub_exclude_files = ['search.html']
smart_quotes = False
The Driver Development Guide
============================
.. warning:: TODO describe the internals
Tools
=====
The driver is distributed with a few tools living in the ``tools/``
subdirectory and they do not use any dedicated library, instead
they do raw accesses to the driver. The programs are meant to provide
examples about the use of the driver interface.
The generic library `adc-lib`_ supports this driver, so you may want to
consider to use the generic tools from that library.
.. _`adc-lib`: http://www.ohwr.org/projects/adc-lib
Trigger Configuration
---------------------
The program ``fau-trg-config`` configures the FMC ADC trigger. The tool
offers command line parameters to configure every register exported by
the driver. The help screen for the program summarizes the options::
# ./tools/fau-trg-config --help
fau-trg-config [OPTIONS] <DEVICE>
<DEVICE>: ZIO name of the device to use
--pre|-p <value>: number of pre samples
--post|-P <value>: number of pre samples
--nshots|-n <value>: number of trigger shots
--delay|-d <value>: set the ticks delay of the trigger
--threshold|-t <value>: set internal trigger threshold
--channel|-c <value>: select the internal channel as trigger
--external: set to external trigger. The default is the internal trigger.
--negative-edge: set internal trigger polarity to negative edge. The default
is positive edge.
--enable-sw-trg: enable the software trigger. By default is disabled.
--disable-hw-trg: disable the hardware trigger. By default is enabled
--force: force all attribute to the program default
--help|-h: show this help
NOTE: The software trigger works only if also hardware trigger is enabled
The tool gets the configuration values from the user and it writes them
to the corresponding sysfs attributes for the specified device. or
example, if you want to configure the board for the external trigger and
3 shots of 10 pre-samples and 100 post-samples, this is the associated
command line::
# ./tools/fau-trg-config --external --pre 10 --post 100 --re-enable 2 \
adc-100m14b-0200
As shown, the nshot parameter is passed as a number of re-enables,
because the trigger is initially automatically enabled. This may change
in the future, for better naming consistency with hardware documentation
and across tools.
Acquisition Time
----------------
The program fau-acq-time retrieves the timestamps associated with the
acquisition. This is the help screen of the program::
./tools/fau-acq-time --help
fau-acq-time [OPTIONS] <DEVICE>
<DEVICE>: ZIO name of the device to use
--last|-l : time between the last trigger and the acquisition end
--full|-f : time between the acquisition start and the acquisition end
--help|-h: show this help
The program can return two different *types* of acquisition time. The
value returned by **last** represent the time elapsed between the last
trigger event and the acquire-end event; this is the time spent during
the last capture.
The value returned by **full** is the time elapsed between the
acquisition start event and the acquisition end event, i.e. the total
time spent waiting for all trigger events and the time spent acquiring
all samples.
Channel Configuration
---------------------
The program ``tools/fau-config-if`` is a simple graphic tool that allow to
select offset and range for the four channels, activate termination and
see the current value of each channel, every 500ms.
The program open one window for each detected card, and configures it by
writing to sysfs. Such writes are also reported to stdout (in the
terminal where you invoked the program), so you can easily copy the
pathnames in your shell commands.
The figure below shows two instances of the tool, running on the same card
with device_id 0x200 (your window decorations will be different, according
to your choice of window manager or desktop environment).
The first one (at the left) is running under Tk-8.5; the second one shows
the graphic appearance of Tk-8.4 (and earlier versions). If you prefer the
older one, run *wish8.4 tools/fau-config-if* instead of
``tools/fau-config-if`` (or set the previous version as default Tk interpreter).
.. figure:: img/config-if.gif
:alt: Two snapshots of fa-config-if
:align: center
Parallel Port Burst
-------------------
If you have a Parallel Port you can use it to generate bursts of pulses
with a software program. This may be useful to test the external
trigger; you can connect the parallel port to the external trigger of
the FMC ADC and generate your trigger events with this program
The program parport-burst, part of this package, generates a burst
according to three command line parameters: the I/O port of the data
byte of the parallel port, the repeat count and the duration of each
period. This example makes 1000 pulses of 100 usec each, using the
physical address of my parallel port (if yours is part of the
motherboard, the address is ``378``)::
./tools/parport-burst dd00 1000 100
This diff is collapsed.
Welcome to FMC ADC 100M 14 bit 4 Channel's documentation!
=========================================================
This is the user manual of the driver for the `FMC ADC 100M 14b 4cha`_
board developed on the `Open Hardware Repository`_. FMC is the form
factor of the card, ADC is its role, 100M means it can acquire
100Msample per second, 14b is the numbers of meaningful bits and 4cha
states it has 4 input channels (plus a trigger input).
The project has been splitted in sub-projects, one for each development
domain: `hardware`_, `gateware`_ and `software`_.
.. _`Open Hardware Repository`: http://www.ohwr.org/
.. _`FMC ADC 100M 14b 4cha`: http://www.ohwr.org/projects/fmc-adc-100m14b4cha
.. _`hardware`: https://www.ohwr.org/projects/fmc-adc-100m14b4cha-hw
.. _`gateware`: https://www.ohwr.org/projects/fmc-adc-100m14b4cha-gw
.. _`software`: https://www.ohwr.org/projects/fmc-adc-100m14b4cha-sw
.. toctree::
:maxdepth: 2
:caption: Contents:
driver-user
driver-devel
driver-tools
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
......@@ -21,17 +21,17 @@ FMC_PARAM_GATEWARE(fa_dev_drv);
static int fa_enable_test_data_fpga;
module_param_named(enable_test_data_fpga, fa_enable_test_data_fpga, int, 0444);
static int fa_enable_test_data_adc;
int fa_enable_test_data_adc = 0;
module_param_named(enable_test_data_adc, fa_enable_test_data_adc, int, 0444);
static int fa_enable_test_data_adc_pattern = 0x555;
module_param_named(enable_test_data_adc_pattern, fa_enable_test_data_adc_pattern, int, 0664);
static const int zfad_hw_range[] = {
[FA100M14B4C_RANGE_10V] = 0x45,
[FA100M14B4C_RANGE_1V] = 0x11,
[FA100M14B4C_RANGE_100mV] = 0x23,
[FA100M14B4C_RANGE_OPEN] = 0x00,
[FA100M14B4C_RANGE_10V_CAL] = 0x44,
[FA100M14B4C_RANGE_1V_CAL] = 0x40,
[FA100M14B4C_RANGE_100mV_CAL] = 0x42,
[FA100M14B4C_RANGE_10V] = 0x45,
[FA100M14B4C_RANGE_1V] = 0x11,
[FA100M14B4C_RANGE_100mV] = 0x23,
[FA100M14B4C_RANGE_OPEN] = 0x00,
};
/* fmc-adc specific workqueue */
......@@ -94,24 +94,63 @@ int zfad_pattern_data_enable(struct fa_dev *fa, uint16_t pattern,
return 0;
}
/**
* It sets the DAC voltage to apply an offset on the input channel
* @chan
* @val DAC values (-5V: 0x0000, 0V: 0x8000, +5V: 0x7FFF)
*
* Return: 0 on success, otherwise a negative error number
*/
static int zfad_dac_set(struct zio_channel *chan, uint32_t val)
{
struct fa_dev *fa = get_zfadc(&chan->cset->zdev->head.dev);
return fa_spi_xfer(fa, FA_SPI_SS_DAC(chan->index), 16, val, NULL);
}
static int zfad_offset_to_dac(struct zio_channel *chan,
int32_t uval,
enum fa100m14b4c_input_range range)
{
struct fa_dev *fa = get_zfadc(&chan->cset->zdev->head.dev);
int offset, gain, hwval;
hwval = uval * 0x8000 / 5000000;
if (hwval == 0x8000)
hwval = 0x7fff; /* -32768 .. 32767 */
offset = fa->calib.dac[range].offset[chan->index];
gain = fa->calib.dac[range].gain[chan->index];
hwval = ((hwval + offset) * gain) >> 15; /* signed */
hwval += 0x8000; /* offset binary */
if (hwval < 0)
hwval = 0;
if (hwval > 0xffff)
hwval = 0xffff;
return hwval;
}
/*
* zfad_apply_user_offset
* @fa: the fmc-adc descriptor
* @chan: the channel where apply offset
* @usr_val: the offset value to apply, expressed as millivolts (-5000..5000)
*
* Apply user offset to the channel input. Before apply the user offset it must
* be corrected with offset and gain calibration value. An open input does not
* need any correction.
* be corrected with offset and gain calibration value.
*
* Offset values are taken from `struct fa_dev`, so they must be there before
* calling this function
*/
int zfad_apply_user_offset(struct fa_dev *fa, struct zio_channel *chan,
uint32_t usr_val)
int zfad_apply_offset(struct zio_channel *chan)
{
struct fa_dev *fa = get_zfadc(&chan->cset->zdev->head.dev);
uint32_t range_reg;
int32_t uval = (int32_t)usr_val;
int offset, gain, hwval, i, range;
int32_t off_uv;
int hwval, i, range;
if (uval < -5000 || uval > 5000)
off_uv = fa->user_offset[chan->index] + fa->zero_offset[chan->index];
if (off_uv < -5000000 || off_uv > 5000000)
return -EINVAL;
i = zfad_get_chx_index(ZFA_CHx_CTL_RANGE, chan);
......@@ -121,27 +160,13 @@ int zfad_apply_user_offset(struct fa_dev *fa, struct zio_channel *chan,
if (range < 0)
return range;
if (range == FA100M14B4C_RANGE_OPEN) {
offset = FA_CAL_NO_OFFSET;
gain = FA_CAL_NO_GAIN;
} else {
offset = fa->calib.dac[range].offset[chan->index];
gain = fa->calib.dac[range].gain[chan->index];
}
hwval = uval * 0x8000 / 5000;
if (hwval == 0x8000)
hwval = 0x7fff; /* -32768 .. 32767 */
hwval = ((hwval + offset) * gain) >> 15; /* signed */
hwval += 0x8000; /* offset binary */
if (hwval < 0)
hwval = 0;
if (hwval > 0xffff)
hwval = 0xffff;
if (range == FA100M14B4C_RANGE_OPEN || fa_enable_test_data_adc)
range = FA100M14B4C_RANGE_1V;
else if (range >= FA100M14B4C_RANGE_10V_CAL)
range -= FA100M14B4C_RANGE_10V_CAL;
/* Apply calibrated offset to DAC */
return fa_spi_xfer(fa, FA_SPI_SS_DAC(chan->index), 16, hwval, NULL);
hwval = zfad_offset_to_dac(chan, off_uv, range);
return zfad_dac_set(chan, hwval);
}
/*
......@@ -154,8 +179,11 @@ void zfad_reset_offset(struct fa_dev *fa)
{
int i;
for (i = 0; i < FA100M14B4C_NCHAN; ++i)
zfad_apply_user_offset(fa, &fa->zdev->cset->chan[i], 0);
for (i = 0; i < FA100M14B4C_NCHAN; ++i) {
fa->user_offset[i] = 0;
fa->zero_offset[i] = 0;
zfad_apply_offset(&fa->zdev->cset->chan[i]);
}
}
/*
......@@ -191,25 +219,20 @@ int zfad_set_range(struct fa_dev *fa, struct zio_channel *chan,
i = zfad_get_chx_index(ZFA_CHx_CTL_RANGE, chan);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[i], zfad_hw_range[range]);
if (range == FA100M14B4C_RANGE_OPEN || fa_enable_test_data_adc) {
/*
* With OPEN range we do not use calibration values
*
* In test mode we do not apply the gain/offset because
* this compromises the test pattern
*/
offset = FA_CAL_NO_OFFSET;
gain = FA_CAL_NO_GAIN;
} else {
if (range < 0 || range > ARRAY_SIZE(fa->calib.adc)) {
dev_info(fa->msgdev, "Invalid range %i or ch %i\n",
range, chan->index);
return -EINVAL;
}
offset = fa->calib.adc[range].offset[chan->index];
gain = fa->calib.adc[range].gain[chan->index];
if (range == FA100M14B4C_RANGE_OPEN || fa_enable_test_data_adc)
range = FA100M14B4C_RANGE_1V;
else if (range >= FA100M14B4C_RANGE_10V_CAL)
range -= FA100M14B4C_RANGE_10V_CAL;
if (range < 0 || range > ARRAY_SIZE(fa->calib.adc)) {
dev_info(fa->msgdev, "Invalid range %i or ch %i\n",
range, chan->index);
return -EINVAL;
}
offset = fa->calib.adc[range].offset[chan->index];
gain = fa->calib.adc[range].gain[chan->index];
i = zfad_get_chx_index(ZFA_CHx_OFFSET, chan);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[i],
offset & 0xffff /* prevent warning */);
......@@ -217,7 +240,7 @@ int zfad_set_range(struct fa_dev *fa, struct zio_channel *chan,
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[i], gain);
/* recalculate user offset for the new range */
zfad_apply_user_offset(fa, chan, fa->user_offset[chan->index]);
zfad_apply_offset(chan);
return 0;
}
......@@ -234,7 +257,6 @@ int zfad_fsm_command(struct fa_dev *fa, uint32_t command)
{
struct zio_cset *cset = fa->zdev->cset;
uint32_t val;
int err;
if (command != FA100M14B4C_CMD_START &&
command != FA100M14B4C_CMD_STOP) {
......@@ -300,20 +322,8 @@ int zfad_fsm_command(struct fa_dev *fa, uint32_t command)
dev_dbg(fa->msgdev, "FSM START Command, Enable interrupts\n");
fa_enable_irqs(fa);
/*
* Set the test data if necessary. This is unlikely to happen,
* and when this is the case we do not care about performances
*/
if (unlikely(fa_enable_test_data_adc)) {
fa_enable_test_data_adc_pattern &= 0xFFF;
err = zfad_pattern_data_enable(fa, fa_enable_test_data_adc_pattern,
fa_enable_test_data_adc);
if (err)
dev_warn(fa->msgdev,
"Failed to set the ADC test data. Continue without\n");
else if (fa_enable_test_data_adc)
dev_info(fa->msgdev, "the ADC test data is enabled on all channels\n");
}
fa_writel(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFA_CTL_RST_TRG_STA], 1);
} else {
dev_dbg(fa->msgdev, "FSM STOP Command, Disable interrupts\n");
fa->enable_auto_start = 0;
......@@ -404,29 +414,17 @@ static int __fa_init(struct fa_dev *fa)
/* Enable mezzanine clock */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFA_CTL_CLK_EN], 1);
/* Set decimation to minimum */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_SR_DECI], 1);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_SR_UNDER], 1);
/* Set test data register */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFA_CTL_TEST_DATA_EN],
fa_enable_test_data_fpga);
/* Set to single shot mode by default */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_SHOTS_NB], 1);
if (zdev->cset->ti->cset->trig == &zfat_type) {
/* Select external trigger (index 0) */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_HW_SEL],
1);
zdev->cset->ti->zattr_set.ext_zattr[FA100M14B4C_TATTR_EXT].value = 1;
fa->trig_compensation = FA_CH_TX_DELAY;
} else {
/* Enable Software trigger*/
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SW_EN],
1);
/* Disable Hardware trigger*/
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_HW_EN],
0);
/* Set default trigger delay */
fa->trig_compensation = 0;
}
/* Enable the software trigger by default: there is no arm in this */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SRC],
FA100M14B4C_TRG_SRC_SW);
/* Zero offsets and release the DAC clear */
zfad_reset_offset(fa);
......@@ -439,12 +437,7 @@ static int __fa_init(struct fa_dev *fa)
fa_writel(fa, fa->fa_utc_base, &zfad_regs[ZFA_UTC_SECONDS],
get_seconds());
/*
* Set Trigger delay in order to compensate
* the channel signal transmission delay
*/
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_DLY],
fa->trig_compensation);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_EXT_DLY], 0);
/* disable auto_start */
fa->enable_auto_start = 0;
......
......@@ -53,8 +53,7 @@ int zfad_dma_start(struct zio_cset *cset)
* Disable all triggers to prevent fires between
* different DMA transfers required for multi-shots
*/
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_HW_EN], 0);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SW_EN], 0);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SRC], 0);
/* Fix dev_mem_addr in single-shot mode */
if (fa->n_shots == 1) {
......@@ -124,13 +123,16 @@ void zfad_dma_done(struct zio_cset *cset)
ctrl = zio_get_ctrl(block);
trig_timetag = (uint32_t *)(block->data + block->datalen
- FA_TRIG_TIMETAG_BYTES);
/* Timetag marker (metadata) used for debugging */
dev_dbg(fa->msgdev, "trig_timetag metadata of the shot %d"
" (expected value: 0x6fc8ad2d): 0x%x\n",
i, *trig_timetag);
ctrl->tstamp.secs = *(++trig_timetag);
ctrl->tstamp.ticks = *(++trig_timetag);
ctrl->tstamp.bins = *(++trig_timetag);
if (unlikely((*(trig_timetag + 1) >> 8) != 0xACCE55))
dev_err(fa->msgdev,
"Wrong acquisition TAG, expected 0xACCE55 but got 0x%X (0x%X)\n",
(*(trig_timetag + 1) >> 8), *trig_timetag);
ctrl->tstamp.secs = ((uint64_t)*(trig_timetag + 1) & 0xFF) << 32;
ctrl->tstamp.secs |= *(trig_timetag);
ctrl->tstamp.ticks = *(trig_timetag + 2);
ctrl->tstamp.bins = 0;
ctrl->attr_trigger.ext_val[FA100M14B4C_TATTR_STA]= *(trig_timetag + 3);
/* Acquisition start Timetag */
ctrl->attr_channel.ext_val[FA100M14B4C_DATTR_ACQ_START_S] =
......@@ -159,24 +161,8 @@ void zfad_dma_done(struct zio_cset *cset)
dev_dbg(fa->msgdev, "%i blocks transfered\n", fa->n_shots);
zio_trigger_data_done(cset);
/*
* we can safely re-enable triggers.
* Hardware trigger depends on the enable status
* of the trigger. Software trigger depends on the previous
* status taken form zio attributes (index 5 of extended one)
* If the user is using a software trigger, enable the software
* trigger.
*/
if (cset->trig == &zfat_type) {
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_HW_EN],
(ti->flags & ZIO_STATUS ? 0 : 1));
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SW_EN],
ti->zattr_set.ext_zattr[6].value);
} else {
dev_dbg(fa->msgdev, "Software acquisition over\n");
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SW_EN],
1);
}
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SRC],
ti->zattr_set.ext_zattr[FA100M14B4C_TATTR_SRC].value);
}
......
......@@ -16,69 +16,83 @@ const struct zfa_field_desc zfad_regs[] = {
[ZFA_CTL_TEST_DATA_EN] = {0x00, 0x00000020, 1},
[ZFA_CTL_TRIG_LED] = {0x00, 0x00000040, 1},
[ZFA_CTL_ACQ_LED] = {0x00, 0x00000080, 1},
[ZFA_CTL_RST_TRG_STA] = {0x00, 0x00000100, 1},
/* Status registers */
[ZFA_STA_FSM] = {0x04, 0x00000007, 1},
[ZFA_STA_FSM] = {0x04, 0x00000007, 1},
[ZFA_STA_SERDES_PLL] = {0x04, 0x00000008, 1},
[ZFA_STA_SERDES_SYNCED] = {0x04, 0x00000010, 1},
/* Trigger */
/* Config register */
[ZFAT_CFG_HW_SEL] = {0x08, 0x00000001, 1},
[ZFAT_CFG_HW_POL] = {0x08, 0x00000002, 1},
[ZFAT_CFG_HW_EN] = {0x08, 0x00000004, 1},
[ZFAT_CFG_SW_EN] = {0x08, 0x00000008, 1},
[ZFAT_CFG_INT_SEL] = {0x08, 0x00000030, 1},
[ZFAT_CFG_THRES_FILT] = {0x08, 0x0000FF00, 1},
[ZFAT_CFG_THRES] = {0x08, 0xFFFF0000, 1},
[ZFAT_CFG_STA] = {0x08, 0xFFFFFFFF, 0},
[ZFAT_CFG_SRC] = {0x0C, 0xFFFFFFFF, 0},
[ZFAT_CFG_POL] = {0x10, 0xFFFFFFFF, 0},
/* Delay */
[ZFAT_DLY] = {0x0C, 0xFFFFFFFF, 0},
[ZFAT_EXT_DLY] = {0x14, 0xFFFFFFFF, 0},
/* Software */
[ZFAT_SW] = {0x10, 0xFFFFFFFF, 0},
[ZFAT_SW] = {0x18, 0xFFFFFFFF, 0},
/* Number of shots */
[ZFAT_SHOTS_NB] = {0x14, 0x0000FFFF, 0},
[ZFAT_SHOTS_NB] = {0x1C, 0x0000FFFF, 0},
/* Multishot max samples*/
[ZFA_MULT_MAX_SAMP] = {0x20, 0xFFFFFFFF, 0},
/* Remaining shots counter */
[ZFAT_SHOTS_REM] = {0x18, 0x0000FFFF, 0},
[ZFAT_SHOTS_REM] = {0x24, 0x0000FFFF, 0},
/* Position address */
[ZFAT_POS] = {0x28, 0xFFFFFFFF, 0},
/* Sampling clock frequency */
[ZFAT_SAMPLING_HZ] = {0x20, 0xFFFFFFFF, 0},
[ZFAT_SAMPLING_HZ] = {0x2C, 0xFFFFFFFF, 0},
/* Sample rate */
[ZFAT_SR_DECI] = {0x24, 0xFFFFFFFF, 0},
/* Position address */
[ZFAT_POS] = {0x1C, 0xFFFFFFFF, 0},
[ZFAT_SR_UNDER] = {0x30, 0xFFFFFFFF, 0},
/* Pre-sample */
[ZFAT_PRE] = {0x28, 0xFFFFFFFF, 0},
[ZFAT_PRE] = {0x34, 0xFFFFFFFF, 0},
/* Post-sample */
[ZFAT_POST] = {0x2C, 0xFFFFFFFF, 0},
[ZFAT_POST] = {0x38, 0xFFFFFFFF, 0},
/* Sample counter */
[ZFAT_CNT] = {0x30, 0xFFFFFFFF, 0},
[ZFAT_CNT] = {0x3C, 0xFFFFFFFF, 0},
/* Channel 1 */
[ZFA_CH1_CTL_RANGE] = {0x34, 0x00000077, 1},
[ZFA_CH1_CTL_TERM] = {0x34, 0x00000008, 1},
[ZFA_CH1_STA] = {0x38, 0x0000FFFF, 0},
[ZFA_CH1_GAIN] = {0x3C, 0x0000FFFF, 0},
[ZFA_CH1_OFFSET] = {0x40, 0x0000FFFF, 0},
[ZFA_CH1_SAT] = {0x44, 0x00007FFF, 0},
[ZFA_CH1_CTL_RANGE] = {0x80, 0x00000077, 1},
[ZFA_CH1_CTL_TERM] = {0x80, 0x00000008, 1},
[ZFA_CH1_STA] = {0x84, 0x0000FFFF, 0},
[ZFA_CH1_GAIN] = {0x88, 0x0000FFFF, 0},
[ZFA_CH1_OFFSET] = {0x8C, 0x0000FFFF, 0},
[ZFA_CH1_SAT] = {0x90, 0x00007FFF, 0},
[ZFA_CH1_HYST] = {0x94, 0xFFFF0000, 1},
[ZFA_CH1_THRES] = {0x94, 0x0000FFFF, 1},
[ZFA_CH1_DLY] = {0x98, 0xFFFFFFFF, 0},
/* Channel 2 */
[ZFA_CH2_CTL_RANGE] = {0x48, 0x00000077, 1},
[ZFA_CH2_CTL_TERM] = {0x48, 0x00000008, 1},
[ZFA_CH2_STA] = {0x4C, 0x0000FFFF, 0},
[ZFA_CH2_GAIN] = {0x50, 0x0000FFFF, 0},
[ZFA_CH2_OFFSET] = {0x54, 0x0000FFFF, 0},
[ZFA_CH2_SAT] = {0x58, 0x00007FFF, 0},
[ZFA_CH2_CTL_RANGE] = {0x100, 0x00000077, 1},
[ZFA_CH2_CTL_TERM] = {0x100, 0x00000008, 1},
[ZFA_CH2_STA] = {0x104, 0x0000FFFF, 0},
[ZFA_CH2_GAIN] = {0x108, 0x0000FFFF, 0},
[ZFA_CH2_OFFSET] = {0x10C, 0x0000FFFF, 0},
[ZFA_CH2_SAT] = {0x110, 0x00007FFF, 0},
[ZFA_CH2_HYST] = {0x114, 0xFFFF0000, 1},
[ZFA_CH2_THRES] = {0x114, 0x0000FFFF, 1},
[ZFA_CH2_DLY] = {0x118, 0xFFFFFFFF, 0},
/* Channel 3 */
[ZFA_CH3_CTL_RANGE] = {0x5C, 0x00000077, 1},
[ZFA_CH3_CTL_TERM] = {0x5C, 0x00000008, 1},
[ZFA_CH3_STA] = {0x60, 0x0000FFFF, 0},
[ZFA_CH3_GAIN] = {0x64, 0x0000FFFF, 0},
[ZFA_CH3_OFFSET] = {0x68, 0x0000FFFF, 0},
[ZFA_CH3_SAT] = {0x6C, 0x00007FFF, 0},
[ZFA_CH3_CTL_RANGE] = {0x180, 0x00000077, 1},
[ZFA_CH3_CTL_TERM] = {0x180, 0x00000008, 1},
[ZFA_CH3_STA] = {0x184, 0x0000FFFF, 0},
[ZFA_CH3_GAIN] = {0x188, 0x0000FFFF, 0},
[ZFA_CH3_OFFSET] = {0x18C, 0x0000FFFF, 0},
[ZFA_CH3_SAT] = {0x190, 0x00007FFF, 0},
[ZFA_CH3_HYST] = {0x194, 0xFFFF0000, 1},
[ZFA_CH3_THRES] = {0x194, 0x0000FFFF, 1},
[ZFA_CH3_DLY] = {0x198, 0xFFFFFFFF, 0},
/* Channel 4 */
[ZFA_CH4_CTL_RANGE] = {0x70, 0x00000077, 1},
[ZFA_CH4_CTL_TERM] = {0x70, 0x00000008, 1},
[ZFA_CH4_STA] = {0x74, 0x0000FFFF, 0},
[ZFA_CH4_GAIN] = {0x78, 0x0000FFFF, 0},
[ZFA_CH4_OFFSET] = {0x7C, 0x0000FFFF, 0},
[ZFA_CH4_SAT] = {0x80, 0x00007FFF, 0},
/* Other options */
[ZFA_MULT_MAX_SAMP] = {0x84, 0xFFFFFFFF, 0},
[ZFA_CH4_CTL_RANGE] = {0x200, 0x00000077, 1},
[ZFA_CH4_CTL_TERM] = {0x200, 0x00000008, 1},
[ZFA_CH4_STA] = {0x204, 0x0000FFFF, 0},
[ZFA_CH4_GAIN] = {0x208, 0x0000FFFF, 0},
[ZFA_CH4_OFFSET] = {0x20C, 0x0000FFFF, 0},
[ZFA_CH4_SAT] = {0x210, 0x00007FFF, 0},
[ZFA_CH4_HYST] = {0x214, 0xFFFF0000, 1},
[ZFA_CH4_THRES] = {0x214, 0x0000FFFF, 1},
[ZFA_CH4_DLY] = {0x218, 0xFFFFFFFF, 0},
/* IRQ */
[ZFA_IRQ_ADC_DISABLE_MASK] = {0x00, 0x00000003, 0},
[ZFA_IRQ_ADC_ENABLE_MASK] = {0x04, 0x00000003, 0},
......@@ -88,12 +102,13 @@ const struct zfa_field_desc zfad_regs[] = {
[ZFA_IRQ_VIC_ENABLE_MASK] = {0x08, 0x00000003, 0},
[ZFA_IRQ_VIC_DISABLE_MASK] = {0x0C, 0x00000003, 0},
[ZFA_IRQ_VIC_MASK_STATUS] = {0x10, 0x00000003, 0},
/* UTC */
[ZFA_UTC_SECONDS] = {0x00, ~0x0, 0},
[ZFA_UTC_COARSE] = {0x04, ~0x0, 0},
[ZFA_UTC_TRIG_META] = {0x08, ~0x0, 0},
[ZFA_UTC_TRIG_SECONDS] = {0x0C, ~0x0, 0},
[ZFA_UTC_TRIG_COARSE] = {0x10, ~0x0, 0},
[ZFA_UTC_TRIG_COARSE] = {0x10, ~0x0, 0},
[ZFA_UTC_TRIG_FINE] = {0x14, ~0x0, 0},
[ZFA_UTC_ACQ_START_META] = {0x18, ~0x0, 0},
[ZFA_UTC_ACQ_START_SECONDS] = {0x1C, ~0x0, 0},
......
......@@ -42,12 +42,13 @@ static int fa_spec_init(struct fa_dev *fa)
cdata->fa_irq_dma_base, cdata->fa_dma_base,
fa->fa_carrier_csr_base);
/* Wait 50ms, so device has time to calibrate */
/* Reset the FMC slot */
fa_writel(fa, fa->fa_carrier_csr_base,
&fa_spec_regs[ZFA_CAR_FMC_RES], 1);
mdelay(50);
/* set FMC0 in normal FMC operation */
fa_writel(fa, fa->fa_carrier_csr_base,
&fa_spec_regs[ZFA_CAR_FMC_RES], 1);
&fa_spec_regs[ZFA_CAR_FMC_RES], 0);
mdelay(50);
/* Verify that the FMC is plugged (0 is plugged) */
val = fa_readl(fa, fa->fa_carrier_csr_base,
......
......@@ -41,14 +41,13 @@ static int fa_svec_init(struct fa_dev *fa)
0x10006610,
fmc->slot_id, NULL);
if (fmc->slot_id == 0)
/* set FMC0 in normal FMC operation */
fa_writel(fa, fa->fa_carrier_csr_base,
&fa_svec_regfield[FA_CAR_FMC0_RES], 1);
else if (fmc->slot_id == 1)
/* set FMC1 in normal FMC operation */
fa_writel(fa, fa->fa_carrier_csr_base,
&fa_svec_regfield[FA_CAR_FMC1_RES], 1);
/* Reset the FMC slot*/
fa_writel(fa, fa->fa_carrier_csr_base,
&fa_svec_regfield[FA_CAR_FMC0_RES + fmc->slot_id], 1);
mdelay(50);
fa_writel(fa, fa->fa_carrier_csr_base,
&fa_svec_regfield[FA_CAR_FMC0_RES + fmc->slot_id], 0);
mdelay(50);
/* register carrier data */
fa->carrier_data = cdata;
......
......@@ -19,7 +19,6 @@
ZIO_PARAM_BUFFER(adc_buffer);
/*
* zio device attributes
*/
......@@ -41,13 +40,18 @@ static struct zio_attribute zfad_cset_ext_zattr[] = {
* acquisition you can decimate samples. 0 is a forbidden value, 1
* for the maximum speed.
*/
ZIO_ATTR_EXT("sample-decimation", ZIO_RW_PERM, ZFAT_SR_DECI, 1),
ZIO_ATTR_EXT("undersample", ZIO_RW_PERM, ZFAT_SR_UNDER, 1),
ZIO_ATTR_EXT("ch0-offset", ZIO_RW_PERM, ZFA_CH1_OFFSET, 0),
ZIO_ATTR_EXT("ch1-offset", ZIO_RW_PERM, ZFA_CH2_OFFSET, 0),
ZIO_ATTR_EXT("ch2-offset", ZIO_RW_PERM, ZFA_CH3_OFFSET, 0),
ZIO_ATTR_EXT("ch3-offset", ZIO_RW_PERM, ZFA_CH4_OFFSET, 0),
ZIO_ATTR_EXT("ch0-offset-zero", ZIO_RW_PERM, ZFA_SW_CH1_OFFSET_ZERO, 0),
ZIO_ATTR_EXT("ch1-offset-zero", ZIO_RW_PERM, ZFA_SW_CH2_OFFSET_ZERO, 0),
ZIO_ATTR_EXT("ch2-offset-zero", ZIO_RW_PERM, ZFA_SW_CH3_OFFSET_ZERO, 0),
ZIO_ATTR_EXT("ch3-offset-zero", ZIO_RW_PERM, ZFA_SW_CH4_OFFSET_ZERO, 0),
ZIO_ATTR_EXT("ch0-vref", ZIO_RW_PERM, ZFA_CH1_CTL_RANGE, 0),
ZIO_ATTR_EXT("ch1-vref", ZIO_RW_PERM, ZFA_CH2_CTL_RANGE, 0),
ZIO_ATTR_EXT("ch2-vref", ZIO_RW_PERM, ZFA_CH3_CTL_RANGE, 0),
......@@ -120,6 +124,7 @@ static struct zio_attribute zfad_cset_ext_zattr[] = {
ZIO_PARAM_EXT("sample-frequency", ZIO_RO_PERM, ZFAT_SAMPLING_HZ, 0),
ZIO_PARAM_EXT("max-sample-mshot", ZIO_RO_PERM, ZFA_MULT_MAX_SAMP, 0),
ZIO_PARAM_EXT("sample-counter", ZIO_RO_PERM, ZFAT_CNT, 0),
ZIO_PARAM_EXT("test-data-pattern", ZIO_RW_PERM, ZFAT_ADC_TST_PATTERN, 0),
};
#if 0 /* FIXME Unused until TLV control will be available */
......@@ -167,7 +172,8 @@ static int zfad_conf_set(struct device *dev, struct zio_attribute *zattr,
struct fa_dev *fa = get_zfadc(dev);
unsigned int baseoff = fa->fa_adc_csr_base;
struct zio_channel *chan;
int i, range, err, reg_index;
int i, range, err = 0, reg_index;
uint32_t off;
reg_index = zattr->id;
i = FA100M14B4C_NCHAN;
......@@ -186,6 +192,21 @@ static int zfad_conf_set(struct device *dev, struct zio_attribute *zattr,
case ZFA_SW_R_NOADDERS_AUTO:
fa->enable_auto_start = usr_val;
return 0;
case ZFA_SW_CH1_OFFSET_ZERO:
i--;
case ZFA_SW_CH2_OFFSET_ZERO:
i--;
case ZFA_SW_CH3_OFFSET_ZERO:
i--;
case ZFA_SW_CH4_OFFSET_ZERO:
i--;
chan = to_zio_cset(dev)->chan + i;
fa->zero_offset[i] = usr_val;
err = zfad_apply_offset(chan);
if (err == -EIO)
fa->zero_offset[chan->index] = 0;
return err;
case ZFA_CHx_SAT:
/* TODO when TLV */
break;
......@@ -209,25 +230,24 @@ static int zfad_conf_set(struct device *dev, struct zio_attribute *zattr,
i--;
case ZFA_CH4_OFFSET:
i--;
chan = to_zio_cset(dev)->chan + i;
err = zfad_apply_user_offset(fa, chan, usr_val);
if (err)
return err;
fa->user_offset[chan->index] = usr_val;
return 0;
err = zfad_apply_offset(chan);
if (err == -EIO)
fa->user_offset[chan->index] = 0;
return err;
case ZFA_CHx_OFFSET:
chan = to_zio_chan(dev),
err = zfad_apply_user_offset(fa, chan, usr_val);
if (err)
return err;
fa->user_offset[chan->index] = usr_val;
return 0;
err = zfad_apply_offset(chan);
if (err == -EIO)
fa->user_offset[chan->index] = 0;
return err;
case ZFA_CTL_DAC_CLR_N:
zfad_reset_offset(fa);
return 0;
case ZFAT_SR_DECI:
case ZFAT_SR_UNDER:
if (usr_val == 0)
usr_val++;
break;
......@@ -261,9 +281,6 @@ static int zfad_conf_set(struct device *dev, struct zio_attribute *zattr,
return range;
return zfad_set_range(fa, to_zio_chan(dev), range);
case ZFA_CHx_STA:
reg_index = zfad_get_chx_index(reg_index, to_zio_chan(dev));
break;
case ZFA_UTC_COARSE:
if (usr_val >= FA100M14B4C_UTC_CLOCK_FREQ) {
dev_err(fa->msgdev,
......@@ -274,6 +291,24 @@ static int zfad_conf_set(struct device *dev, struct zio_attribute *zattr,
break;
case ZFA_CTL_FMS_CMD:
return zfad_fsm_command(fa, usr_val);
case ZFAT_ADC_TST_PATTERN:
if (unlikely(fa_enable_test_data_adc)) {
usr_val &= 0xFFF;
err = zfad_pattern_data_enable(fa, usr_val,
fa_enable_test_data_adc);
if (err)
dev_warn(fa->msgdev,
"Failed to set the ADC test data. Continue without\n");
else if (fa_enable_test_data_adc)
dev_info(fa->msgdev,
"the ADC test data (0x%x) is enabled on all channels\n",
usr_val);
return err;
} else {
dev_err(fa->msgdev,
"Cannot set the ADC test data. The driver is not in test mode\n");
return -EPERM;
}
}
fa_writel(fa, baseoff, &zfad_regs[reg_index], usr_val);
......@@ -313,7 +348,7 @@ static int zfad_info_get(struct device *dev, struct zio_attribute *zattr,
case ZFA_CHx_OFFSET:
*usr_val = fa->user_offset[to_zio_chan(dev)->index];
return 0;
case ZFAT_ADC_TST_PATTERN:
case ZFA_SW_R_NOADDRES_NBIT:
case ZFA_SW_R_NOADDERS_AUTO:
/* ZIO automatically return the attribute value */
......@@ -326,19 +361,28 @@ static int zfad_info_get(struct device *dev, struct zio_attribute *zattr,
*usr_val = fa_read_temp(fa, 0);
*usr_val = (*usr_val * 1000 + 8) / 16;
return 0;
case ZFA_SW_CH1_OFFSET_ZERO:
i--;
case ZFA_SW_CH2_OFFSET_ZERO:
i--;
case ZFA_SW_CH3_OFFSET_ZERO:
i--;
case ZFA_SW_CH4_OFFSET_ZERO:
i--;
*usr_val = fa->zero_offset[i];
return 0;
case ZFA_CHx_SAT:
case ZFA_CHx_CTL_TERM:
case ZFA_CHx_CTL_RANGE:
reg_index = zfad_get_chx_index(zattr->id, to_zio_chan(dev));
break;
case ZFA_CHx_STA:
reg_index = zfad_get_chx_index(zattr->id, to_zio_chan(dev));
*usr_val = fa_readl(fa, fa->fa_adc_csr_base,
&zfad_regs[reg_index]);
i = (int16_t)(*usr_val); /* now signed integer */
*usr_val = i;
break;
return 0;
default:
reg_index = zattr->id;
}
......@@ -491,11 +535,16 @@ static void zfad_stop_cset(struct zio_cset *cset)
static int zfad_zio_probe(struct zio_device *zdev)
{
struct fa_dev *fa = zdev->priv_d;
int err;
dev_dbg(fa->msgdev, "%s:%d\n", __func__, __LINE__);
/* Save also the pointer to the real zio_device */
fa->zdev = zdev;
err = zfad_pattern_data_enable(fa, 0, fa_enable_test_data_adc);
if (err)
return err;
/* We don't have csets at this point, so don't do anything more */
return 0;
}
......
......@@ -30,43 +30,62 @@ static ZIO_ATTR_DEFINE_STD(ZIO_TRG, zfat_std_zattr) = {
ZIO_ATTR(trig, ZIO_ATTR_TRIG_POST_SAMP, ZIO_RW_PERM, ZFAT_POST, 1),
};
static struct zio_attribute zfat_ext_zattr[] = {
/* Config register */
/* Hardware trigger selction
* 0: internal (data threshold)
* 1: external (front panel trigger input)
*/
[FA100M14B4C_TATTR_EXT] = ZIO_ATTR_EXT("external", ZIO_RW_PERM,
ZFAT_CFG_HW_SEL, 0),
/*
* Internal Hardware trigger polarity
* 0: positive edge/slope
* 1: negative edge/slope
*/
[FA100M14B4C_TATTR_STA] = ZIO_ATTR_EXT("source-triggered", ZIO_RW_PERM,
ZFAT_CFG_STA, 0),
[FA100M14B4C_TATTR_SRC] = ZIO_ATTR_EXT("source", ZIO_RW_PERM,
ZFAT_CFG_SRC,
FA100M14B4C_TRG_SRC_SW),
[FA100M14B4C_TATTR_POL] = ZIO_ATTR_EXT("polarity", ZIO_RW_PERM,
ZFAT_CFG_HW_POL, 0),
/*
* Channel selection for internal trigger
* 0: channel 1, 1: channel 2, 2: channel 3, 3: channel 4
*/
[FA100M14B4C_TATTR_INT_CHAN] = ZIO_ATTR_EXT("int-channel",
ZIO_RW_PERM, ZFAT_CFG_INT_SEL, 0),
ZFAT_CFG_POL, 0),
/* Internal trigger threshold value is 2 complement format */
[FA100M14B4C_TATTR_INT_THRES] = ZIO_ATTR_EXT("int-threshold",
ZIO_RW_PERM, ZFAT_CFG_THRES, 0),
[FA100M14B4C_TATTR_CH1_THRES] = ZIO_ATTR_EXT("ch0-threshold",
ZIO_RW_PERM,
ZFA_CH1_THRES, 0),
[FA100M14B4C_TATTR_CH2_THRES] = ZIO_ATTR_EXT("ch1-threshold",
ZIO_RW_PERM,
ZFA_CH2_THRES, 0),
[FA100M14B4C_TATTR_CH3_THRES] = ZIO_ATTR_EXT("ch2-threshold",
ZIO_RW_PERM,
ZFA_CH3_THRES, 0),
[FA100M14B4C_TATTR_CH4_THRES] = ZIO_ATTR_EXT("ch3-threshold",
ZIO_RW_PERM,
ZFA_CH4_THRES, 0),
[FA100M14B4C_TATTR_CH1_HYST] = ZIO_ATTR_EXT("ch0-hysteresis",
ZIO_RW_PERM,
ZFA_CH1_HYST, 0),
[FA100M14B4C_TATTR_CH2_HYST] = ZIO_ATTR_EXT("ch1-hysteresis",
ZIO_RW_PERM,
ZFA_CH2_HYST, 0),
[FA100M14B4C_TATTR_CH3_HYST] = ZIO_ATTR_EXT("ch2-hysteresis",
ZIO_RW_PERM,
ZFA_CH3_HYST, 0),
[FA100M14B4C_TATTR_CH4_HYST] = ZIO_ATTR_EXT("ch3-hysteresis",
ZIO_RW_PERM,
ZFA_CH4_HYST, 0),
[FA100M14B4C_TATTR_CH1_DLY] = ZIO_ATTR_EXT("ch0-delay",
ZIO_RW_PERM,
ZFA_CH1_DLY, 0),
[FA100M14B4C_TATTR_CH2_DLY] = ZIO_ATTR_EXT("ch1-delay",
ZIO_RW_PERM,
ZFA_CH2_DLY, 0),
[FA100M14B4C_TATTR_CH3_DLY] = ZIO_ATTR_EXT("ch2-delay",
ZIO_RW_PERM,
ZFA_CH3_DLY, 0),
[FA100M14B4C_TATTR_CH4_DLY] = ZIO_ATTR_EXT("ch3-delay",
ZIO_RW_PERM,
ZFA_CH4_DLY, 0),
/*
* Delay to apply on the trigger in sampling clock period. The default
* clock frequency is 100MHz (period = 10ns)
*/
[FA100M14B4C_TATTR_DELAY] = ZIO_ATTR_EXT("delay", ZIO_RW_PERM,
ZFAT_DLY, 0),
[FA100M14B4C_TATTR_EXT_DLY] = ZIO_ATTR_EXT("ext-delay", ZIO_RW_PERM,
ZFAT_EXT_DLY, 0),
/* setup the maximum glith length to filter */
ZIO_ATTR_EXT("int-threshold-filter", ZIO_RW_PERM, ZFAT_CFG_THRES_FILT,
0),
/* Software Trigger */
/* Enable (1) or disable (0) software trigger */
[FA100M14B4C_TATTR_SW_EN] = ZIO_PARAM_EXT("sw-trg-enable", ZIO_RW_PERM,
ZFAT_CFG_SW_EN, 0),
[FA100M14B4C_TATTR_SW_FIRE] = ZIO_PARAM_EXT("sw-trg-fire", ZIO_WO_PERM,
ZFAT_SW, 0),
......@@ -90,7 +109,7 @@ static int zfat_conf_set(struct device *dev, struct zio_attribute *zattr,
{
struct fa_dev *fa = get_zfadc(dev);
struct zio_ti *ti = to_zio_ti(dev);
uint32_t tmp_val = usr_val, delay;
uint32_t tmp_val = usr_val;
switch (zattr->id) {
case ZFAT_SHOTS_NB:
......@@ -108,7 +127,8 @@ static int zfat_conf_set(struct device *dev, struct zio_attribute *zattr,
break;
case ZFAT_SW:
/* Fire if software trigger is enabled (index 5) */
if (!ti->zattr_set.ext_zattr[FA100M14B4C_TATTR_SW_EN].value) {
if (!(ti->zattr_set.ext_zattr[FA100M14B4C_TATTR_SRC].value &
FA100M14B4C_TRG_SRC_SW)) {
dev_info(fa->msgdev, "sw trigger is not enabled\n");
return -EPERM;
}
......@@ -123,20 +143,16 @@ static int zfat_conf_set(struct device *dev, struct zio_attribute *zattr,
* acquisition or other problems:
*/
break;
case ZFAT_DLY:
/* Add channel signal transmission delay */
tmp_val += fa->trig_compensation;
break;
case ZFAT_CFG_HW_SEL:
/* Remove old compensation value */
delay = fa_readl(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_DLY]);
delay -= fa->trig_compensation;
/* Calculate and apply new compensation */
fa->trig_compensation = tmp_val ? FA_CH_TX_DELAY : 0;
delay += fa->trig_compensation;
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_DLY], delay);
break;
case ZFAT_CFG_SRC:
/*
* Do not copy to hardware when globally disabled
* We tell ZIO to save the value locally and will do
* it when the user starts an acquisition
*
* We cannot save the value in cache only when disabled
* because the trigger is always disabled during configuration
*/
return 0;
}
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[zattr->id], tmp_val);
......@@ -153,15 +169,20 @@ static int zfat_info_get(struct device *dev, struct zio_attribute *zattr,
{
struct fa_dev *fa = get_zfadc(dev);
switch (zattr->id) {
case ZFAT_CFG_SRC:
/*
* The good value for the trigger source is always in
* the ZIO cache.
*/
return 0;
}
*usr_val = fa_readl(fa, fa->fa_adc_csr_base, &zfad_regs[zattr->id]);
switch (zattr->id) {
case ZFAT_POST:
(*usr_val)++; /* add the trigger sample */
break;
case ZFAT_DLY:
/* Add channel signal transmission delay */
*usr_val -= fa->trig_compensation;
break;
}
return 0;
......@@ -190,11 +211,6 @@ static struct zio_ti *zfat_create(struct zio_trigger_type *trig,
if (!zfat)
return ERR_PTR(-ENOMEM);
/* Disable Software trigger*/
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SW_EN], 0);
/* Enable Hardware trigger*/
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_HW_EN], 1);
zfat->fa = fa;
zfat->ti.cset = cset;
......@@ -206,10 +222,9 @@ static void zfat_destroy(struct zio_ti *ti)
struct fa_dev *fa = ti->cset->zdev->priv_d;
struct zfat_instance *zfat = to_zfat_instance(ti);
/* Enable Software trigger */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SW_EN], 1);
/* Disable Hardware trigger */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_HW_EN], 0);
/* Disable all trigger sources */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SRC], 0);
/* Other triggers cannot use pre-samples */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_PRE], 0);
/* Reset post samples */
......@@ -222,15 +237,22 @@ static void zfat_destroy(struct zio_ti *ti)
/*
*
* Enable or disable the hardware trigger. The hardware trigger is the prefered
* trigger so it correspond to the ZIO enable of the trigger.Status is active
* low on ZIO but active high on the FMC-ADC, then use '!' on status
* Enable or disable the trigger sources globally.
* On disable (status > 0), we disable all the trigger sources
* On enable (status == 0), we enable the trigger soruces specified in the
* correspondent sysfs attribute
*/
static void zfat_change_status(struct zio_ti *ti, unsigned int status)
{
struct fa_dev *fa = ti->cset->zdev->priv_d;
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_HW_EN], !status);
uint32_t src = ti->zattr_set.ext_zattr[FA100M14B4C_TATTR_SRC].value;
if (status)
fa_writel(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFAT_CFG_SRC], 0);
else
fa_writel(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFAT_CFG_SRC], src);
}
/*
......@@ -291,7 +313,7 @@ static int zfat_arm_trigger(struct zio_ti *ti)
struct zio_block *block;
struct zfad_block *zfad_block;
unsigned int size;
uint32_t dev_mem_off;
uint32_t dev_mem_off, trg_src;
int i, err = 0;
dev_dbg(fa->msgdev, "Arming trigger\n");
......@@ -366,6 +388,10 @@ static int zfat_arm_trigger(struct zio_ti *ti)
if (err != -EAGAIN && err != 0)
goto out_allocate;
/* Everything looks fine for the time being, enable the trigger sources */
trg_src = ti->zattr_set.ext_zattr[FA100M14B4C_TATTR_SRC].value;
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SRC], trg_src);
return err;
out_allocate:
......
......@@ -12,6 +12,25 @@
#define BIT(nr) (1UL << (nr))
#endif
/* Trigger sources */
#define FA100M14B4C_TRG_SRC_EXT BIT(0)
#define FA100M14B4C_TRG_SRC_SW BIT(1)
#define FA100M14B4C_TRG_SRC_TIM BIT(4)
#define FA100M14B4C_TRG_SRC_CH1 BIT(8)
#define FA100M14B4C_TRG_SRC_CH2 BIT(9)
#define FA100M14B4C_TRG_SRC_CH3 BIT(10)
#define FA100M14B4C_TRG_SRC_CH4 BIT(11)
#define FA100M14B4C_TRG_SRC_CHx(_x) (FA100M14B4C_TRG_SRC_CH1 << ((_x) - 1))
/* Trigger Polarity */
#define FA100M14B4C_TRG_POL_EXT FA100M14B4C_TRG_SRC_EXT
#define FA100M14B4C_TRG_POL_CH1 FA100M14B4C_TRG_SRC_CH1
#define FA100M14B4C_TRG_POL_CH2 FA100M14B4C_TRG_SRC_CH2
#define FA100M14B4C_TRG_POL_CH3 FA100M14B4C_TRG_SRC_CH3
#define FA100M14B4C_TRG_POL_CH4 FA100M14B4C_TRG_SRC_CH4
#define FA100M14B4C_TRG_POL_CHx(_x) (FA100M14B4C_TRG_POL_CH1 << ((_x) - 1))
/*
* Trigger Extended Attribute Enumeration
*/
......@@ -24,13 +43,24 @@ enum fa100m14b4c_trg_ext_attr {
* The parameters are not exposed to user space by zio_controle, so it
* is not necessary to export to user space the correspondent enum
*/
FA100M14B4C_TATTR_EXT = 0,
FA100M14B4C_TATTR_STA = 0,
FA100M14B4C_TATTR_SRC,
FA100M14B4C_TATTR_POL,
FA100M14B4C_TATTR_INT_CHAN,
FA100M14B4C_TATTR_INT_THRES,
FA100M14B4C_TATTR_DELAY,
FA100M14B4C_TATTR_EXT_DLY,
FA100M14B4C_TATTR_CH1_THRES,
FA100M14B4C_TATTR_CH2_THRES,
FA100M14B4C_TATTR_CH3_THRES,
FA100M14B4C_TATTR_CH4_THRES,
FA100M14B4C_TATTR_CH1_HYST,
FA100M14B4C_TATTR_CH2_HYST,
FA100M14B4C_TATTR_CH3_HYST,
FA100M14B4C_TATTR_CH4_HYST,
FA100M14B4C_TATTR_CH1_DLY,
FA100M14B4C_TATTR_CH2_DLY,
FA100M14B4C_TATTR_CH3_DLY,
FA100M14B4C_TATTR_CH4_DLY,
#ifdef __KERNEL__
FA100M14B4C_TATTR_SW_EN,
FA100M14B4C_TATTR_SW_FIRE,
FA100M14B4C_TATTR_TRG_S,
FA100M14B4C_TATTR_TRG_C,
......@@ -77,7 +107,10 @@ enum fa100m14b4c_input_range {
FA100M14B4C_RANGE_10V = 0x0,
FA100M14B4C_RANGE_1V,
FA100M14B4C_RANGE_100mV,
FA100M14B4C_RANGE_OPEN, /* Channel disconnected from ADC */
FA100M14B4C_RANGE_OPEN, /* Channel disconnected from ADC */
FA100M14B4C_RANGE_10V_CAL, /* Channel disconnected from ADC */
FA100M14B4C_RANGE_1V_CAL, /* Channel disconnected from ADC */
FA100M14B4C_RANGE_100mV_CAL, /* Channel disconnected from ADC */
};
enum fa100m14b4c_fsm_cmd {
......@@ -111,11 +144,13 @@ enum fa100m14b4c_fsm_state {
#include "field-desc.h"
extern int fa_enable_test_data_adc;
/*
* ZFA_CHx_MULT : the trick which requires channel regs id grouped and ordered
* address offset between two registers of the same type on consecutive channel
*/
#define ZFA_CHx_MULT 6
#define ZFA_CHx_MULT 9
/* Device registers */
enum zfadc_dregs_enum {
......@@ -128,20 +163,17 @@ enum zfadc_dregs_enum {
ZFA_CTL_TEST_DATA_EN,
ZFA_CTL_TRIG_LED,
ZFA_CTL_ACQ_LED,
ZFA_CTL_RST_TRG_STA,
/* Status registers */
ZFA_STA_FSM,
ZFA_STA_SERDES_PLL,
ZFA_STA_SERDES_SYNCED,
/* Configuration register */
ZFAT_CFG_HW_SEL,
ZFAT_CFG_HW_POL,
ZFAT_CFG_HW_EN,
ZFAT_CFG_SW_EN,
ZFAT_CFG_INT_SEL,
ZFAT_CFG_THRES,
ZFAT_CFG_THRES_FILT,
ZFAT_CFG_STA,
ZFAT_CFG_SRC,
ZFAT_CFG_POL,
/* Delay*/
ZFAT_DLY,
ZFAT_EXT_DLY,
/* Software */
ZFAT_SW,
/* Number of shots */
......@@ -149,7 +181,7 @@ enum zfadc_dregs_enum {
/* Remaining shots counter */
ZFAT_SHOTS_REM,
/* Sample rate */
ZFAT_SR_DECI,
ZFAT_SR_UNDER,
/* Sampling clock frequency */
ZFAT_SAMPLING_HZ,
/* Position address */
......@@ -160,6 +192,8 @@ enum zfadc_dregs_enum {
ZFAT_POST,
/* Sample counter */
ZFAT_CNT,
/* Pattern data for the ADC chip */
ZFAT_ADC_TST_PATTERN,
/* start:declaration block requiring some order */
/* Channel 1 */
ZFA_CH1_CTL_RANGE,
......@@ -168,6 +202,10 @@ enum zfadc_dregs_enum {
ZFA_CH1_GAIN,
ZFA_CH1_OFFSET,
ZFA_CH1_SAT,
ZFA_CH1_THRES,
ZFA_CH1_HYST,
ZFA_CH1_DLY,
/* Channel 2 */
ZFA_CH2_CTL_RANGE,
ZFA_CH2_CTL_TERM,
......@@ -175,6 +213,10 @@ enum zfadc_dregs_enum {
ZFA_CH2_GAIN,
ZFA_CH2_OFFSET,
ZFA_CH2_SAT,
ZFA_CH2_THRES,
ZFA_CH2_HYST,
ZFA_CH2_DLY,
/* Channel 3 */
ZFA_CH3_CTL_RANGE,
ZFA_CH3_CTL_TERM,
......@@ -182,6 +224,10 @@ enum zfadc_dregs_enum {
ZFA_CH3_GAIN,
ZFA_CH3_OFFSET,
ZFA_CH3_SAT,
ZFA_CH3_THRES,
ZFA_CH3_HYST,
ZFA_CH3_DLY,
/* Channel 4 */
ZFA_CH4_CTL_RANGE,
ZFA_CH4_CTL_TERM,
......@@ -189,6 +235,10 @@ enum zfadc_dregs_enum {
ZFA_CH4_GAIN,
ZFA_CH4_OFFSET,
ZFA_CH4_SAT,
ZFA_CH4_THRES,
ZFA_CH4_HYST,
ZFA_CH4_DLY,
/*
* CHx__ are specifc ids used by some internal arithmetic
* Be carefull: the arithmetic expects
......@@ -202,7 +252,9 @@ enum zfadc_dregs_enum {
ZFA_CHx_GAIN,
ZFA_CHx_OFFSET,
ZFA_CHx_SAT,
ZFA_CHx_THRES,
ZFA_CHx_HYST,
ZFA_CHx_DLY,
/* Other options */
ZFA_MULT_MAX_SAMP,
/* end:declaration block requiring some order */
......@@ -237,9 +289,11 @@ enum zfadc_dregs_enum {
ZFA_HW_PARAM_COMMON_LAST,
};
/* trigger timestamp block size in bytes */
/* This block is added after the post trigger samples */
/* in the DDR and contains the trigger timestamp */
/*
* Acquisition metadata. It contains the trigger timestamp and the trigger
* source. This block is added after the post-trigger-samples in the DDR.
*/
#define FA_TRIG_TIMETAG_BYTES 0x10
/*
......@@ -252,6 +306,10 @@ enum fa_sw_param_id {
ZFA_SW_R_NOADDRES_TEMP,
ZFA_SW_R_NOADDERS_AUTO,
ZFA_SW_CH1_OFFSET_ZERO,
ZFA_SW_CH2_OFFSET_ZERO,
ZFA_SW_CH3_OFFSET_ZERO,
ZFA_SW_CH4_OFFSET_ZERO,
ZFA_SW_PARAM_COMMON_LAST,
};
......@@ -315,7 +373,8 @@ struct fa_calib {
* @n_fires: number of trigger fire occurred within an acquisition
*
* @n_dma_err: number of errors
*
* @user_offset: user offset (micro-Volts)
* @zero_offset: necessary offset to push the channel to zero (micro-Volts)
*/
struct fa_dev {
struct device *msgdev; /**< device used to print messages */
......@@ -359,8 +418,8 @@ struct fa_dev {
unsigned int n_dma_err;
/* Configuration */
int user_offset[4]; /* one per channel */
int32_t user_offset[4]; /* one per channel */
int32_t zero_offset[FA100M14B4C_NCHAN];
/* one-wire */
uint8_t ds18_id[8];
unsigned long next_t;
......@@ -372,8 +431,6 @@ struct fa_dev {
/* flag */
int enable_auto_start;
uint32_t trig_compensation;
struct dentry *reg_dump;
};
......@@ -496,8 +553,7 @@ extern const struct zfa_field_desc zfad_regs[];
/* Functions exported by fa-core.c */
extern int zfad_fsm_command(struct fa_dev *fa, uint32_t command);
extern int zfad_apply_user_offset(struct fa_dev *fa, struct zio_channel *chan,
uint32_t usr_val);
extern int zfad_apply_offset(struct zio_channel *chan);
extern void zfad_reset_offset(struct fa_dev *fa);
extern int zfad_convert_hw_range(uint32_t bitmask);
extern int zfad_set_range(struct fa_dev *fa, struct zio_channel *chan,
......
......@@ -35,13 +35,13 @@ signed long fmc_find_sdb_device_ext(struct sdb_array *tree,
c = &r->dev.sdb_component;
p = &c->product;
if (!IS_ERR(tree->subtree[i]))
if (!IS_ERR(tree->subtree[i])) {
/* FIXME: this index SHOULD be recursive, too */
res = fmc_find_sdb_device(tree->subtree[i],
vid, did, sz);
/* FIXME: this index SHOULD be recursive, too */
if (ci == index && res >= 0)
return res + tree->baseaddr;
if (res >= 0 && ci++ == index)
return res + tree->baseaddr;
}
if (r->empty.record_type != sdb_type_device)
continue;
if (__be64_to_cpu(p->vendor_id) != vid)
......@@ -53,9 +53,8 @@ signed long fmc_find_sdb_device_ext(struct sdb_array *tree,
first = __be64_to_cpu(c->addr_first);
if (sz)
*sz = (typeof(*sz)) (last + 1 - first);
if (ci == index)
if (ci++ == index)
return first + tree->baseaddr;
ci++;
}
return res;
}
.depend
\ No newline at end of file
# This is not a kbuild Makefile. It is a plain Makefile so it can be copied
# If it exists includes Makefile.specific. In this Makefile, you should put
# specific Makefile code that you want to run before this. For example,
# build a particular environment.
-include Makefile.specific
# include parent_common.mk for buildsystem's defines
REPO_PARENT ?= ../..
-include $(REPO_PARENT)/parent_common.mk
ZIO ?= ../zio
ZIO_ABS ?= $(abspath $(ZIO) )
GIT_VERSION := $(shell git describe --dirty --long --tags)
ZIO_GIT_VERSION := $(shell cd $(ZIO_ABS); git describe --dirty --long --tags)
LIB = libfmcadc.a
LOBJ := route.o
LOBJ += init.o
LOBJ += boards.o
LOBJ += config-zio.o
LOBJ += buffer-zio.o
LOBJ += lib.o
LOBJ += fmc-adc-100m14b4cha.o
CFLAGS = -Wall -ggdb -O2 -fPIC -I../kernel -I$(ZIO_ABS)/include $(EXTRACFLAGS)
CFLAGS += -DGIT_VERSION="\"$(GIT_VERSION)\""
CFLAGS += -DZIO_GIT_VERSION="\"$(ZIO_GIT_VERSION)\""
LDFLAGS = -L. -lfmcadc
CC ?= $(CROSS_COMPILE)gcc
modules all: $(LIB)
%: %.c $(LIB)
$(CC) $(CFLAGS) $*.c $(LDFLAGS) -o $@
$(LIB): $(LOBJ)
$(AR) r $@ $^
clean:
rm -f $(LIB) .depend *.o *~
.depend: Makefile $(wildcard *.c *.h ../*.h)
$(CC) $(CFLAGS) -M $(LOBJ:.o=.c) -o $@
install modules_install:
-include .depend
/*
* All the boards in the library
*
* Copyright (C) 2013 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include "fmcadc-lib.h"
#include "fmcadc-lib-int.h"
#define FMCADC_ZIO_TRG_MASK (1LL << FMCADC_CONF_TRG_SOURCE) | \
(1LL << FMCADC_CONF_TRG_SOURCE_CHAN) | \
(1LL << FMCADC_CONF_TRG_THRESHOLD) | \
(1LL << FMCADC_CONF_TRG_POLARITY) | \
(1LL << FMCADC_CONF_TRG_DELAY)
#define FMCADC_ZIO_ACQ_MASK (1LL << FMCADC_CONF_ACQ_N_SHOTS) | \
(1LL << FMCADC_CONF_ACQ_POST_SAMP) | \
(1LL << FMCADC_CONF_ACQ_PRE_SAMP) | \
(1LL << FMCADC_CONF_ACQ_DECIMATION) | \
(1LL << FMCADC_CONF_ACQ_FREQ_HZ) | \
(1LL << FMCADC_CONF_ACQ_N_BITS)
#define FMCADC_ZIO_CHN_MASK (1LL << FMCADC_CONF_CHN_RANGE) | \
(1LL << FMCADC_CONF_CHN_TERMINATION) | \
(1LL << FMCADC_CONF_CHN_OFFSET)
#define FMCADC_ZIO_BRD_MASK (1LL << FMCADC_CONF_BRD_STATE_MACHINE_STATUS) | \
(1LL << FMCADC_CONF_BRD_N_CHAN) | \
(1LL << FMCADC_CONF_UTC_TIMING_BASE_S) | \
(1LL << FMCADC_CONF_UTC_TIMING_BASE_T)
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 = "fmc-adc-100m14b4cha", /* for library open() */
.devname = "adc-100m14b", /* for device named in /dev/zio */
.driver_type = "zio",
.capabilities = {
FMCADC_ZIO_TRG_MASK,
FMCADC_ZIO_ACQ_MASK,
FMCADC_ZIO_CHN_MASK,
FMCADC_ZIO_BRD_MASK,
},
.fa_op = &fa_100ms_4ch_14bit_op,
};
/*
* 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)
{
const struct fmcadc_board_type *b;
b = find_board(name);
if (!b)
return NULL;
return b->fa_op->open(b, dev_id, buffersize, nbuffer, flags);
}
#define FMCADC_PATH_PATTERN "/dev/%s.%d"
/* 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)
{
ssize_t ret;
char dev_id_str[8];
char path[PATH_MAX];
int dev_id;
ret = snprintf(path, sizeof(path), "/dev/%s.%d",
"adc-100m14b" /* FIXME: this must be generic */,
lun);
if (ret < 0 || ret >= sizeof(path)) {
errno = EINVAL;
return NULL;
}
ret = readlink(path, dev_id_str, sizeof(dev_id_str));
if (sscanf(dev_id_str, "%4x", &dev_id) != 1) {
errno = ENODEV;
return NULL;
}
return fmcadc_open(name, dev_id, buffersize, nbuffer, flags);
}
int fmcadc_close(struct fmcadc_dev *dev)
{
struct fmcadc_gid *b = (struct fmcadc_gid *)dev;
return b->board->fa_op->close(dev);
}
/*
* ZIO-wide buffer management (device-independent)
*
* Copyright (C) 2013 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <linux/zio-user.h>
#include "fmcadc-lib.h"
#include "fmcadc-lib-int.h"
/* 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;
i = read(fa->fdc, buf->metadata, sizeof(struct zio_control));
switch (i) {
case sizeof(struct zio_control):
return 0; /* ok */
case -1:
if (fa->flags & FMCADC_FLAG_VERBOSE)
fprintf(stderr, "%s: read: %s\n", __func__,
strerror(errno));
return -1;
case 0:
if (fa->flags & FMCADC_FLAG_VERBOSE)
fprintf(stderr, "%s: unexpected EOF\n", __func__);
return -1;
default:
if (fa->flags & FMCADC_FLAG_VERBOSE)
fprintf(stderr, "%s: read: %i bytes (expected %zi)\n",
__func__, i, sizeof(ctrl));
return -1;
}
}
/* 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)
{
struct zio_control *ctrl = buf->metadata;
int datalen;
int samplesize = buf->samplesize; /* Careful: includes n_chan */
int i;
/* 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;
}
/* 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;
}
/* 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 __fmcadc_dev_zio *fa = to_dev_zio(dev);
struct fmcadc_buffer *buf;
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) {
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 *to)
{
struct __fmcadc_dev_zio *fa = to_dev_zio(dev);
struct pollfd p;
int to_ms, ret;
/* So, first sample and blocking read. Wait.. */
p.fd = fa->fdc;
p.events = POLLIN | POLLERR;
if (!to)
to_ms = -1;
else
to_ms = to->tv_sec / 1000 + (to->tv_usec + 500) / 1000;
ret = poll(&p, 1, to_ms);
switch (ret) {
case 0:
errno = EAGAIN;
/* fall through */
case -1:
return -1;
}
if (p.revents & POLLERR) {
errno = FMCADC_EDISABLED;
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)
{
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 *))
{
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;
}
This diff is collapsed.
/*
* The ADC library for the specific card
*
* Copyright (C) 2013 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <linux/zio-user.h>
#include <fmc-adc-100m14b4cha.h>
#include "fmcadc-lib.h"
#include "fmcadc-lib-int.h"
#define ZIO_SYS_PATH "/sys/bus/zio/devices"
#define FMCADC_NCHAN 4
static int fmcadc_flush_input(struct __fmcadc_dev_zio *fa)
{
struct zio_control ctrl;
struct pollfd p = {
.fd = fa->fdc,
.events = POLLIN | POLLERR,
};
int i;
/* Read the control until one is there; data is discarded by zio */
while (1) {
i = poll(&p, 1, 0);
if (i < 0)
return -1;
if ((p.revents & POLLIN) == 0)
return 0;
read(fa->fdc, &ctrl, sizeof(ctrl));
}
}
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;
struct stat st;
char *syspath, *devpath, fname[128];
int udev_zio_dir = 1;
/* 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 */
/* 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 ? "/dev/zio" : "/dev"),
b->devname, dev_id);
/* Sysfs path exists, so device is there, hopefully */
fa = calloc(1, sizeof(*fa));
if (!fa)
goto out_fa_alloc;
fa->sysbase = syspath;
fa->devbase = devpath;
fa->cset = 0;
/* Open char devices */
sprintf(fname, "%s-0-i-ctrl", fa->devbase);
fa->fdc = open(fname, O_RDONLY);
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;
if (flags & FMCADC_F_FLUSH)
if (fmcadc_flush_input(fa) < 0)
goto out_fa_open;
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();
/* Support verbose operation (turn user flag into internal flag)*/
if (flags & FMCADC_F_VERBOSE || getenv("LIB_FMCADC_VERBOSE"))
fa->flags |= FMCADC_FLAG_VERBOSE;
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;
}
int fmcadc_zio_close(struct fmcadc_dev *dev)
{
struct __fmcadc_dev_zio *fa = to_dev_zio(dev);
close(fa->fdc);
close(fa->fdd);
free(fa->sysbase);
free(fa->devbase);
free(fa);
return 0;
}
/* poll is used by start, so it's defined first */
int fmcadc_zio_acq_poll(struct fmcadc_dev *dev,
unsigned int flags, struct timeval *to)
{
struct __fmcadc_dev_zio *fa = to_dev_zio(dev);
struct pollfd p;
int to_ms, ret;
/* So, first sample and blocking read. Wait.. */
p.fd = fa->fdc;
p.events = POLLIN | POLLERR;
if (!to)
to_ms = -1;
else
to_ms = to->tv_sec / 1000 + (to->tv_usec + 500) / 1000;
ret = poll(&p, 1, to_ms);
switch (ret) {
case 0:
errno = EAGAIN;
/* fall through */
case -1:
return -1;
}
if (p.revents & POLLERR) {
errno = FMCADC_EDISABLED;
return -1;
}
return 0;
}
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;
if (flags & FMCADC_F_FLUSH)
if (fmcadc_flush_input(fa) < 0)
return -1;
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; /* hw command for "stop" */
return fa_zio_sysfs_set(fa, "cset0/fsm-command", &cmd);
}
/*
* Copyright CERN 2013
* Author: Federico Vaga <federico.vaga@gmail.com>
*/
#ifndef FMCADC_LIB_INT_H_
#define FMCADC_LIB_INT_H_
/*
* 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);
/*
* The operations structure is the device-specific backend of the library
*/
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
* @name name of the board type, for example "fmc-adc-100MS"
* @devname name of the device in Linux
* @driver_type: the kind of driver that hanlde this kind of board (e.g. ZIO)
* @capabilities bitmask of device capabilities for trigger, channel
* acquisition
* @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_operations *fa_op;
};
/*
* Generic Instance Descriptor
*/
struct fmcadc_gid {
const struct fmcadc_board_type *board;
};
/* Definition of board types */
extern struct fmcadc_board_type fmcadc_100ms_4ch_14bit;
/* Internal structure (ZIO specific, for ZIO drivers only) */
struct __fmcadc_dev_zio {
unsigned int cset;
int fdc;
int fdd;
uint32_t dev_id;
unsigned long flags;
char *devbase;
char *sysbase;
unsigned long samplesize;
unsigned long pagesize;
/* Mandatory field */
struct fmcadc_gid gid;
};
/* Note: bit 16 and up are passed by users, see fmcadc-lib.h */
#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,
unsigned int dev_id,
unsigned long totalsamples,
unsigned int nbuffer,
unsigned long flags);
int fmcadc_zio_close(struct fmcadc_dev *dev);
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);
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 *));
/* The following functions are in config-zio.c */
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);
#endif /* FMCADC_LIB_INT_H_ */
/*
* Copyright CERN 2013
* Author: Federico Vaga <federico.vaga@gmail.com>
*/
#ifndef FMCADC_LIB_H_
#define FMCADC_LIB_H_
#ifdef __cplusplus
#pragma GCC diagnostic ignored "-Wwrite-strings"
extern "C" {
#endif
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
/* Error codes start from 1024 to void conflicting with libc codes */
#define __FMCADC_ERRNO_START 1024
#define FMCADC_ENOP 1024
#define FMCADC_ENOCAP 1025
#define FMCADC_ENOCFG 1026
#define FMCADC_ENOGET 1027
#define FMCADC_ENOSET 1028
#define FMCADC_ENOCHAN 1029
#define FMCADC_ENOMASK 1030
#define FMCADC_EDISABLED 1031
struct fmcadc_dev;
enum fmcadc_supported_board {
FMCADC_100MS_4CH_14BIT,
__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;
struct fmcadc_dev *dev;
void *mapaddr;
unsigned long maplen;
unsigned long flags; /* internal to the library */
};
/* This is exactly the zio_timestamp, there is no depency on zio here */
struct fmcadc_timestamp {
uint64_t secs;
uint64_t ticks;
uint64_t bins;
};
/* The following enum can be use to se the mask of valid configurations */
enum fmcadc_configuration_trigger {
FMCADC_CONF_TRG_SOURCE = 0,
FMCADC_CONF_TRG_SOURCE_CHAN,
FMCADC_CONF_TRG_THRESHOLD,
FMCADC_CONF_TRG_POLARITY,
FMCADC_CONF_TRG_DELAY,
FMCADC_CONF_TRG_THRESHOLD_FILTER,
__FMCADC_CONF_TRG_ATTRIBUTE_LAST_INDEX,
};
enum fmcadc_configuration_acquisition {
FMCADC_CONF_ACQ_N_SHOTS = 0,
FMCADC_CONF_ACQ_POST_SAMP,
FMCADC_CONF_ACQ_PRE_SAMP,
FMCADC_CONF_ACQ_DECIMATION,
FMCADC_CONF_ACQ_FREQ_HZ,
FMCADC_CONF_ACQ_N_BITS,
__FMCADC_CONF_ACQ_ATTRIBUTE_LAST_INDEX,
};
enum fmcadc_configuration_channel {
FMCADC_CONF_CHN_RANGE = 0,
FMCADC_CONF_CHN_TERMINATION,
FMCADC_CONF_CHN_OFFSET,
FMCADC_CONF_CHN_SATURATION,
__FMCADC_CONF_CHN_ATTRIBUTE_LAST_INDEX,
};
enum fmcadc_board_status {
FMCADC_CONF_BRD_STATUS = 0,
FMCADC_CONF_BRD_MAX_FREQ_HZ,
FMCADC_CONF_BRD_MIN_FREQ_HZ,
FMCADC_CONF_BRD_STATE_MACHINE_STATUS,
FMCADC_CONF_BRD_N_CHAN,
FMCADC_CONF_UTC_TIMING_BASE_S,
FMCADC_CONF_UTC_TIMING_BASE_T,
FMCADC_CONF_UTC_TIMING_BASE_B,
__FMCADC_CONF_BRD_ATTRIBUTE_LAST_INDEX,
};
enum fmcadc_configuration_type {
FMCADC_CONF_TYPE_TRG = 0, /* Trigger */
FMCADC_CONF_TYPE_ACQ, /* Acquisition */
FMCADC_CONF_TYPE_CHN, /* Channel */
FMCADC_CONF_TYPE_BRD, /* Board */
__FMCADC_CONF_TYPE_LAST_INDEX,
};
/* @deprecated: old typo, keep it for compatibility */
#define FMCADC_CONT_TYPE_BRD FMCADC_CONF_TYPE_BRD
#define __FMCADC_CONF_LEN 64 /* number of allocated items in each structure */
struct fmcadc_conf {
enum fmcadc_configuration_type type;
uint32_t dev_type;
uint32_t route_to;
uint32_t flags; /* how to identify invalid? */
uint64_t mask;
uint32_t value[__FMCADC_CONF_LEN];
};
static inline void fmcadc_set_conf_mask(struct fmcadc_conf *conf,
unsigned int conf_index)
{
conf->mask |= (1LL << conf_index);
}
/* assign a configuration item, and its mask */
static inline void fmcadc_set_conf(struct fmcadc_conf *conf,
unsigned int conf_index, uint32_t val)
{
conf->value[conf_index] = val;
fmcadc_set_conf_mask(conf, conf_index);
}
/* retieve a configuration item */
static inline int fmcadc_get_conf(struct fmcadc_conf *conf,
unsigned int conf_index,
uint32_t *val)
{
if (conf->mask & (1LL << conf_index)) {
*val = conf->value[conf_index];
return 0;
} else {
return -1;
}
}
/* Flags used in open/acq/config -- note: low-bits are used by lib-int.h */
#define FMCSDC_F_USERMASK 0xffff0000
#define FMCADC_F_FLUSH 0x00010000
#define FMCADC_F_VERBOSE 0x00020000
/*
* Actual functions follow
*/
extern int fmcadc_init(void);
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 totalsamples,
unsigned int nbuffer,
unsigned long flags);
extern struct fmcadc_dev *fmcadc_open_by_lun(char *name, int lun,
unsigned long totalsamples,
unsigned int nbuffer,
unsigned long flags);
extern int fmcadc_close(struct fmcadc_dev *dev);
extern int fmcadc_acq_start(struct fmcadc_dev *dev, unsigned int flags,
struct timeval *timeout);
extern int fmcadc_acq_poll(struct fmcadc_dev *dev, unsigned int flags,
struct timeval *timeout);
extern int fmcadc_acq_stop(struct fmcadc_dev *dev, unsigned int flags);
extern int fmcadc_reset_conf(struct fmcadc_dev *dev, unsigned int flags,
struct fmcadc_conf *conf);
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);
extern int fmcadc_fill_buffer(struct fmcadc_dev *dev,
struct fmcadc_buffer *buf,
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,
struct fmcadc_buffer *buf,
void (*free_fn)(void *));
extern char *fmcadc_get_driver_type(struct fmcadc_dev *dev);
/* libfmcadc version string */
extern const char * const libfmcadc_version_s;
/* zio version string used during compilation of libfmcadc */
extern const char * const libfmcadc_zio_version_s;
static inline int fmcadc_mshot_buf_max_size_get(struct fmcadc_dev *dev,
int *value)
{
return fmcadc_get_param(dev, "cset0/max-sample-mshot", NULL, value);
}
static inline int fmcadc_buffer_type_get(struct fmcadc_dev *dev, char *buf_type)
{
return fmcadc_get_param(dev, "cset0/current_buffer", buf_type, NULL);
}
static inline int fmcadc_buffer_maximum_size_get(struct fmcadc_dev *dev,
int *size)
{
char s[16];
int err;
err = fmcadc_buffer_type_get(dev, s);
if (err)
return -1;
if (!strcmp(s, "vmalloc")) {
return fmcadc_get_param(dev,
"cset0/chani/buffer/max-buffer-kb",
NULL, size);
} else if (!strcmp(s, "kmalloc")) {
return 4 * 1024; /* MiB => KiB */
}
return -1;
}
static inline int fmcadc_buffer_maximum_size_set(struct fmcadc_dev *dev,
int size)
{
char s[16];
int err;
err = fmcadc_buffer_type_get(dev, s);
if (err)
return -1;
if (!strcmp(s, "vmalloc")) {
return fmcadc_set_param(dev,
"cset0/chani/buffer/max-buffer-kb",
NULL, &size);
} else if (!strcmp(s, "kmalloc")) {
return -1; /* cannot be changed */
}
return -1;
}
/**
* Get the current software trigger enable status
* @param[in] dev adc device token
* @param[out] enable enable status
* @return 0 on success. -1 on error and errno is set appropriately
*/
static inline int fmcadc_trigger_sw_status(struct fmcadc_dev *dev,
unsigned int *enable)
{
return fmcadc_get_param(dev, "cset0/trigger/sw-trg-enable",
NULL, (int *)enable);
}
/**
* Set the current software trigger enable status
* @param[in] dev adc device token
* @param[in] enable 0 disable, 1 enable
* @return 0 on success. -1 on error and errno is set appropriately
*/
static inline int fmcadc_trigger_sw_enable(struct fmcadc_dev *dev,
unsigned int enable)
{
int value = !!enable;
return fmcadc_set_param(dev, "cset0/trigger/sw-trg-enable",
NULL, &value);
}
/**
* Execute a software trigger
* @param[in] dev adc device token
* @return 0 on success. -1 on error and errno is set appropriately
*/
static inline int fmcadc_trigger_sw_fire(struct fmcadc_dev *dev)
{
int value = 1;
return fmcadc_set_param(dev, "cset0/trigger/sw-trg-fire",
NULL, &value);
}
#ifdef __cplusplus
}
#endif
#endif /* FMCADC_LIB_H_ */
/*
* Copyright CERN 2013, GNU GPL 2 or later.
* Author: Alessandro Rubini
*/
#include "fmcadc-lib.h"
const char * const libfmcadc_version_s = "libfmcadc version: " GIT_VERSION;
const char * const libfmcadc_zio_version_s = "libfmcadc is using zio version: " ZIO_GIT_VERSION;
/* We currently do nothing in init/exit. We might check /proc/meminfo... */
int fmcadc_init(void)
{
return 0;
}
void fmcadc_exit(void)
{
return;
}
/*
* Initializing and cleaning up the fmc adc library
*
* Copyright (C) 2013 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <string.h>
#include "fmcadc-lib.h"
#include "fmcadc-lib-int.h"
/* * * * * * * * * * * * * * * * Utilities * * * * * * * * * * * * * * * * */
/*
* fmcadc_strerror
* @dev: device for which you want to know the meaning of the error
* @errnum: error number
*/
static struct fmcadc_errors {
int num;
char *str;
} fmcadc_errors[] = {
{ FMCADC_ENOP, "Operation not supported"},
{ FMCADC_ENOCAP, "Capabilities not supported"},
{ FMCADC_ENOCFG, "Configuration type not supported"},
{ FMCADC_ENOGET, "Cannot get capabilities information"},
{ FMCADC_ENOSET, "Cannot set capabilities information"},
{ FMCADC_ENOCHAN, "Invalid channel"},
{ FMCADC_ENOMASK, "Missing configuration mask"},
{ FMCADC_EDISABLED, "Trigger is disabled: I/O aborted"},
{ 0, }
};
char *fmcadc_strerror(int errnum)
{
struct fmcadc_errors *p;
if (errnum < __FMCADC_ERRNO_START)
return strerror(errnum);
for (p = fmcadc_errors; p->num; p++)
if (p->num == errnum)
return p->str;
return "Unknown error code";
}
/*
* fmcadc_get_driver_type
* @dev: device which want to know the driver type
*/
char *fmcadc_get_driver_type(struct fmcadc_dev *dev)
{
struct fmcadc_gid *b = (void *)dev;
return b->board->driver_type;
}
/*
* Routing public functions to device-specific code
*
* Copyright (C) 2013 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "fmcadc-lib.h"
#include "fmcadc-lib-int.h"
int fmcadc_acq_start(struct fmcadc_dev *dev,
unsigned int flags,
struct timeval *timeout)
{
struct fmcadc_gid *g = (struct fmcadc_gid *)dev;
const struct fmcadc_board_type *b = g->board;
return b->fa_op->acq_start(dev, flags, timeout);
}
int fmcadc_acq_poll(struct fmcadc_dev *dev, unsigned int flags,
struct timeval *timeout)
{
struct fmcadc_gid *g = (struct fmcadc_gid *)dev;
const struct fmcadc_board_type *b = g->board;
return b->fa_op->acq_poll(dev, flags, timeout);
}
int fmcadc_acq_stop(struct fmcadc_dev *dev, unsigned int flags)
{
struct fmcadc_gid *g = (struct fmcadc_gid *)dev;
const struct fmcadc_board_type *b = g->board;
return b->fa_op->acq_stop(dev, flags);
}
int fmcadc_apply_config(struct fmcadc_dev *dev, unsigned int flags,
struct fmcadc_conf *conf)
{
struct fmcadc_gid *g = (struct fmcadc_gid *)dev;
const struct fmcadc_board_type *b = g->board;
uint64_t cap_mask;
if (!conf->mask) {
errno = FMCADC_ENOMASK;
return -1; /* Nothing to do */
}
cap_mask = b->capabilities[conf->type];
if ((cap_mask & conf->mask) != conf->mask) {
/* Unsupported capabilities */
errno = FMCADC_ENOCAP;
return -1;
}
return b->fa_op->apply_config(dev, flags, conf);
}
int fmcadc_retrieve_config(struct fmcadc_dev *dev, struct fmcadc_conf *conf)
{
struct fmcadc_gid *g = (struct fmcadc_gid *)dev;
const struct fmcadc_board_type *b = g->board;
uint64_t cap_mask;
if (!conf->mask) {
errno = FMCADC_ENOMASK;
return -1; /* Nothing to do */
}
cap_mask = b->capabilities[conf->type];
if ((cap_mask & conf->mask) != conf->mask) {
/* Unsupported capabilities */
errno = FMCADC_ENOCAP;
return -1;
}
return b->fa_op->retrieve_config(dev, conf);
}
int fmcadc_get_param(struct fmcadc_dev *dev, char *name,
char *sptr, int *iptr)
{
struct fmcadc_gid *g = (struct fmcadc_gid *)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 = (struct fmcadc_gid *)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 *g = (struct fmcadc_gid *)dev;
const struct fmcadc_board_type *b = g->board;
return b->fa_op->request_buffer(dev, nsamples, alloc, flags);
}
int fmcadc_fill_buffer(struct fmcadc_dev *dev,
struct fmcadc_buffer *buf,
unsigned int flags,
struct timeval *timeout)
{
struct fmcadc_gid *g = (struct fmcadc_gid *)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 = (struct fmcadc_gid *)buf->dev;
const struct fmcadc_board_type *b = g->board;
return b->fa_op->tstamp_buffer(buf, ts);
}
int fmcadc_release_buffer(struct fmcadc_dev *dev, struct fmcadc_buffer *buf,
void (*free)(void *))
{
struct fmcadc_gid *g = (struct fmcadc_gid *)dev;
const struct fmcadc_board_type *b = g->board;
if (!buf)
return 0;
return b->fa_op->release_buffer(dev, buf, free);
}
fald-simple-acq
fald-test
fald-simple-get-conf
fald-acq
fald-trg-cfg
fald-bad-clock
\ No newline at end of file
# If it exists includes Makefile.specific. In this Makefile, you should put
# specific Makefile code that you want to run before this. For example,
# build a particular environment.
-include Makefile.specific
# include parent_common.mk for buildsystem's defines
REPO_PARENT ?= ../..
-include $(REPO_PARENT)/parent_common.mk
DESTDIR ?= /usr/local
LIBADC = ../lib/
ZIO ?= ../zio
ZIO_ABS ?= $(abspath $(ZIO) )
GIT_VERSION := $(shell git describe --dirty --long --tags)
ZIO_GIT_VERSION := $(shell cd $(ZIO_ABS); git describe --dirty --long --tags)
CFLAGS = -Wall -g -ggdb -I$(LIBADC) -I$(ZIO_ABS)/include -I../kernel $(EXTRACFLAGS)
CFLAGS += -DGIT_VERSION="\"$(GIT_VERSION)\""
CFLAGS += -DZIO_GIT_VERSION="\"$(ZIO_GIT_VERSION)\""
LDFLAGS = -L$(LIBADC)
LDLIBS = -lfmcadc -lpthread -lrt
DEMOS := fald-simple-acq fald-acq fald-trg-cfg
DEMOS += fald-simple-get-conf
DEMOS += fald-test
DEMOS += fald-bad-clock
all: demo
demo: $(DEMOS)
install:
install -d $(DESTDIR)/bin
install -D $(DEMOS) $(DESTDIR)/bin
%: %.c $(LIBADC)/libfmcadc.a
$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) $(LDLIBS)
# make nothing for modules_install, but avoid errors
modules_install:
clean:
rm -f $(DEMOS) *.o *~
.PHONY: all, clean
This diff is collapsed.
/*
* Copyright CERN 2014
* Author: Federico Vaga <federico.vaga@gmail.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <getopt.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <linux/zio-user.h>
#include <fmcadc-lib.h>
#include <fmc-adc-100m14b4cha.h>
static char git_version[] = "version: " GIT_VERSION;
static char zio_git_version[] = "zio version: " ZIO_GIT_VERSION;
/* Subtract the `struct timespec' values X and Y,
storing the result in RESULT.
Return 1 if the difference is negative, otherwise 0. */
int timespec_subtract (result, x, y)
struct timespec *result, *x, *y;
{
/* Perform the carry for the later subtraction by updating y. */
if (x->tv_nsec < y->tv_nsec) {
int nsec = (y->tv_nsec - x->tv_nsec) / 1000000000 + 1;
y->tv_nsec -= 1000000000 * nsec;
y->tv_sec += nsec;
}
if (x->tv_nsec - y->tv_nsec > 1000000000) {
int nsec = (x->tv_nsec - y->tv_nsec) / 1000000000;
y->tv_nsec += 1000000000 * nsec;
y->tv_sec -= nsec;
}
/* Compute the time remaining to wait.
tv_nsec is certainly positive. */
result->tv_sec = x->tv_sec - y->tv_sec;
result->tv_nsec = x->tv_nsec - y->tv_nsec;
/* Return 1 if result is negative. */
return x->tv_sec < y->tv_sec;
}
static void fald_help()
{
printf("\nfald-bad-clock [OPTIONS] <devid>\n\n");
printf(" -i <seconds> observation interval\n\n");
printf(" -h show this help\n\n");
printf(" -V show version information\n\n");
exit(1);
}
static void print_version(char *pname)
{
printf("%s %s\n", pname, git_version);
printf("%s %s\n", pname, zio_git_version);
printf("%s\n", libfmcadc_version_s);
printf("%s\n", libfmcadc_zio_version_s);
}
int main (int argc, char *argv[])
{
struct fmcadc_dev *adc;
struct fmcadc_conf brd_cfg;
struct timespec sys_start, sys_cur, adc_cur, dlt_ts, dlt_dlt_ts = {0, 0};
int err, devid, interval = 360;
uint32_t adc_sec, adc_ticks;
char c;
/* Prepare the board timing base configuration */
memset(&brd_cfg, 0, sizeof(brd_cfg));
brd_cfg.type = FMCADC_CONT_TYPE_BRD;
while ((c = getopt(argc, argv, "i:hV")) >= 0) {
switch (c) {
case 'i':
err = sscanf(optarg, "%d", &interval);
if (err != 1)
fald_help();
break;
case '?':
case 'h':
fald_help();
break;
case 'V':
print_version(argv[0]);
exit(0);
}
}
sscanf(argv[argc-1], "%x", &devid);
adc = fmcadc_open("fmc-adc-100m14b4cha", devid, 0, 0, FMCADC_F_FLUSH);
if (!adc) {
fprintf(stderr, "%s: cannot open device: %s\n",
argv[0], fmcadc_strerror(errno));
exit(1);
}
do {
err = clock_gettime(CLOCK_REALTIME, &sys_start);
if (err) {
fprintf(stderr, "%s: cannot get real time: %s",
argv[0], strerror(errno));
exit(1);
}
} while (sys_start.tv_nsec > 1000);
/* Configure ADC internal clock */
adc_sec = sys_start.tv_sec;
adc_ticks = sys_start.tv_nsec / FA100M14B4C_UTC_CLOCK_NS;
fmcadc_set_conf(&brd_cfg, FMCADC_CONF_UTC_TIMING_BASE_T, adc_ticks);
fmcadc_set_conf(&brd_cfg, FMCADC_CONF_UTC_TIMING_BASE_S, adc_sec);
err = fmcadc_apply_config(adc, 0 , &brd_cfg);
if (err && errno != FMCADC_ENOMASK) {
fprintf(stderr, "%s: cannot configure board %s\n",
argv[0], fmcadc_strerror(errno));
exit(1);
}
fprintf(stdout,
"ADC clock configured: %010li s %010li ns ( %i %ins ticks)\n",
sys_start.tv_sec, sys_start.tv_nsec,
adc_ticks, FA100M14B4C_UTC_CLOCK_NS);
/* Measure how clock diverge */
while (interval--) {
/* Get the system clock */
err = clock_gettime(CLOCK_REALTIME, &sys_cur);
if (err) {
fprintf(stderr, "%s: cannot get real time: %s",
argv[0], strerror(errno));
exit(1);
}
/* Get the ADC clock */
err = fmcadc_retrieve_config(adc, &brd_cfg);
if (err) {
fprintf(stderr, "%s: cannot get trigger config: %s\n",
argv[0], fmcadc_strerror(errno));
exit(1);
}
fmcadc_get_conf(&brd_cfg, FMCADC_CONF_UTC_TIMING_BASE_S,
&adc_sec);
fmcadc_get_conf(&brd_cfg, FMCADC_CONF_UTC_TIMING_BASE_T,
&adc_ticks);
adc_cur.tv_sec = adc_sec;
adc_cur.tv_nsec = adc_ticks * FA100M14B4C_UTC_CLOCK_NS;
/* Get the difference between system and ADC clock */
timespec_subtract(&dlt_ts, &sys_cur, &adc_cur);
/* How bad is? */
timespec_subtract(&dlt_dlt_ts, &dlt_ts, &dlt_dlt_ts);
/* Show time stamps and delta */
printf(" sys %ld.%.9ld s\n", sys_cur.tv_sec,
sys_cur.tv_nsec);
printf(" adc %ld.%.9ld s\n", adc_cur.tv_sec,
adc_cur.tv_nsec);
printf("|dlt| %ld.%.9ld s (%ld.%.9ld s)\n",
dlt_ts.tv_sec, dlt_ts.tv_nsec,
dlt_dlt_ts.tv_sec, dlt_dlt_ts.tv_nsec);
dlt_dlt_ts = dlt_ts;
if (dlt_ts.tv_sec) {
timespec_subtract(&dlt_dlt_ts, &sys_cur, &sys_start);
printf("Clock diverged of %ld.%.9ld s in %ld.%.9ld s\n",
dlt_ts.tv_sec, dlt_ts.tv_nsec,
dlt_dlt_ts.tv_sec, dlt_dlt_ts.tv_nsec);
break;
}
printf(" sleep 1 s\n");
sleep(1);
}
exit(0);
}
/* Copyright 2013 CERN
* Author: Federico Vaga <federico.vaga@gmail.comZ
* License: GPLv2
*
* This is a simple program to configure the FMC ADC trigger. It is not bug
* aware because it is only a demo program to show you how you can handle the
* trigger.
*/
#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>
static char git_version[] = "version: " GIT_VERSION;
static char zio_git_version[] = "zio version: " ZIO_GIT_VERSION;
static void fald_help()
{
printf("\nfald-simple-acq [OPTIONS] <DEVID>\n\n");
printf(" <DEVID>: hexadecimal identifier (e.g.: \"0x200\")\n");
printf(" --before|-b <num> number of pre samples\n");
printf(" --after|-a <num> n. of post samples (default: 16)\n");
printf(" --nshots|-n <num> number of trigger shots\n");
printf(" --delay|-d <num> delay sample after trigger\n");
printf(" --under-sample|-U <num> pick 1 sample every <num>\n");
printf(" --threshold|-t <num> internal trigger threshold\n");
printf(" --channel|-c <num> channel used 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");
printf(" --dont-read|-N config-only, use with zio-dump\n");
printf(" --version|-V print version information\n");
printf(" --help|-h show this help\n\n");
}
static int trgval[__FMCADC_CONF_LEN]; /* FIXME: this is not used */
static struct option options[] = {
{"before", required_argument, 0, 'b'},
{"after", required_argument, 0, 'a'},
{"nshots", required_argument, 0, 'n'},
{"delay", required_argument, 0, 'd'},
{"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 */
{"binary", required_argument, 0, 'B'},
{"multi-binary",required_argument, 0, 'M'},
{"dont-read", no_argument, 0, 'N'},
/* backward-compatible options */
{"pre", required_argument, 0, 'p'},
{"post", required_argument, 0, 'P'},
{"decimation", required_argument, 0, 'D'},
{"version", no_argument, 0, 'V'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
#define GETOPT_STRING "b:a:n:d:u:t:c:T:B:M:Np:P:D:Vh"
static void print_version(char *pname)
{
printf("%s %s\n", pname, git_version);
printf("%s %s\n", pname, zio_git_version);
printf("%s\n", libfmcadc_version_s);
printf("%s\n", libfmcadc_zio_version_s);
}
int main(int argc, char *argv[])
{
struct fmcadc_buffer *buf;
struct fmcadc_dev *adc;
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];
FILE *f = NULL;
if (argc == 1) {
fald_help();
exit(1);
}
/* reset attributes and provide defaults */
memset(&trg, 0, sizeof(trg));
trg.type = FMCADC_CONF_TYPE_TRG;
fmcadc_set_conf(&trg, FMCADC_CONF_TRG_SOURCE, 1); /* external */
memset(&acq, 0, sizeof(acq));
acq.type = FMCADC_CONF_TYPE_ACQ;
fmcadc_set_conf(&acq, FMCADC_CONF_ACQ_POST_SAMP, postsamples);
fmcadc_set_conf(&acq, FMCADC_CONF_ACQ_N_SHOTS, nshots);
/* Parse options */
while ((c = getopt_long(argc, argv, GETOPT_STRING,
options, &opt_index)) >=0) {
switch (c) {
case 'b': case 'p': /* before */
presamples = atoi(optarg);
fmcadc_set_conf(&acq, FMCADC_CONF_ACQ_PRE_SAMP,
presamples);
break;
case 'a': case 'P': /* after */
postsamples = atoi(optarg);
fmcadc_set_conf(&acq, FMCADC_CONF_ACQ_POST_SAMP,
postsamples);
break;
case 'n':
nshots = atoi(optarg);
fmcadc_set_conf(&acq, FMCADC_CONF_ACQ_N_SHOTS,
nshots);
break;
case 'd':
fmcadc_set_conf(&trg, FMCADC_CONF_TRG_DELAY,
atoi(optarg));
break;
case 'u': case 'D':
fmcadc_set_conf(&acq, FMCADC_CONF_ACQ_DECIMATION,
atoi(optarg));
break;
case 't':
fmcadc_set_conf(&trg, FMCADC_CONF_TRG_THRESHOLD,
atoi(optarg));
break;
case 'c':
/* set internal, and then the channel */
fmcadc_set_conf(&trg, FMCADC_CONF_TRG_SOURCE, 0);
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;
break;
case 'M':
binmode = 2; /* do many binaries */
basefile = optarg;
break;
case 'N':
binmode = -1;
break;
case 'V':
print_version(argv[0]);
exit(0);
case 'h': case '?':
fald_help();
exit(1);
break;
}
}
if (optind != argc - 1) {
fprintf(stderr, "%s: DEVICE-ID is a mandatory argument\n",
argv[0]);
fald_help();
exit(1);
} else {
sscanf(argv[optind], "%x", &dev_id);
}
/* Open the ADC */
adc = fmcadc_open("fmc-adc-100m14b4cha", dev_id,
nshots * (presamples + postsamples),
nshots,
FMCADC_F_FLUSH);
if (!adc) {
fprintf(stderr, "%s: cannot open device: %s\n",
argv[0], fmcadc_strerror(errno));
exit(1);
}
if (strcmp(fmcadc_get_driver_type(adc), "zio")) {
fprintf(stderr, "%s: not a zio driver, aborting\n", argv[0]);
exit(1);
}
/* If we save to a a file, open it now to error out soon */
if (binmode > 0) {
char *s;
s = basefile;
if (binmode == 2) {
sprintf(fname, "%s.000.ctrl", basefile);
s = fname;
}
f = fopen(s, "a");
if (!f) {
fprintf(stderr, "%s: %s: %s\n", argv[0], s,
strerror(errno));
exit(1);
}
if (binmode == 2)
fclose(f);
}
/* Configure trigger (pick trigger polarity from external array) */
fmcadc_set_conf(&trg, FMCADC_CONF_TRG_POLARITY,
trgval[FMCADC_CONF_TRG_POLARITY]);
err = fmcadc_apply_config(adc, 0 , &trg);
if (err && errno != FMCADC_ENOMASK) {
fprintf(stderr, "%s: cannot configure trigger: %s\n",
argv[0], fmcadc_strerror(errno));
exit(1);
}
/* Configure acquisition parameter */
err = fmcadc_apply_config(adc, 0 , &acq);
if (err && errno != FMCADC_ENOMASK) {
fprintf(stderr, "%s: cannot configure acquisition: %s\n",
argv[0], fmcadc_strerror(errno));
exit(1);
}
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);
}
/* 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;
int16_t *data;
if (binmode < 0) /* no data must be acquired */
break;
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));
exit(1);
}
ctrl = buf->metadata;
data = buf->data;
fprintf(stderr, "Read %d samples from shot %i/%i\n",
ctrl->nsamples,
i + 1, acq.value[FMCADC_CONF_ACQ_N_SHOTS]);
if (binmode == 1) { /* append everything to a single file */
if (fwrite(ctrl, sizeof(*ctrl), 1, f) != 1)
err++;
if (fwrite(data, ctrl->ssize, ctrl->nsamples, f)
!= ctrl->nsamples)
err++;
if (err) {
fprintf(stderr, "%s: write(%s): short write\n",
argv[0], basefile);
exit(1);
}
continue; /* next shot please */
}
if (binmode == 2) { /* several files */
sprintf(fname, "%s.%03i.ctrl", basefile, i);
f = fopen(fname, "w");
if (!f) {
fprintf(stderr, "%s: %s: %s\n",
argv[0], fname, strerror(errno));
exit(1);
}
if (fwrite(ctrl, sizeof(*ctrl), 1, f) != 1) {
fprintf(stderr, "%s: write(%s): short write\n",
argv[0], fname);
exit(1);
}
fclose(f);
sprintf(fname, "%s.%03i.data", basefile, i);
f = fopen(fname, "w");
if (!f) {
fprintf(stderr, "%s: %s: %s\n",
argv[0], fname, strerror(errno));
exit(1);
}
if (fwrite(data, ctrl->ssize, ctrl->nsamples, f)
!= ctrl->nsamples) {
fprintf(stderr, "%s: write(%s): short write\n",
argv[0], fname);
exit(1);
}
fclose(f);
continue;
}
/*
* Finally, binmode = 0.
* We lazily know samplesize is 2 bytes and chcount is 4
*/
for (j = 0; j < ctrl->nsamples / 4; j++) {
printf("%5i ", j - presamples);
for (ch = 0; ch < 4; ch++)
printf("%7i", *(data++));
printf("\n");
}
}
if (binmode == 1)
fclose(f);
fmcadc_release_buffer(adc, buf, NULL);
fmcadc_close(adc);
exit(0);
}
This diff is collapsed.
This diff is collapsed.
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#define FALD_TRG_ARGC 2
static char git_version[] = "version: " GIT_VERSION;
static char zio_git_version[] = "zio version: " ZIO_GIT_VERSION;
void send_config(int fd, char *msg)
{
/* removing newline at the end */
if (msg[strlen(msg)-1] == '\n')
msg[strlen(msg)-1] = '\0';
if (strlen(msg)) {
write(fd, msg, strlen(msg));
fprintf(stdout, "New trig setting sent: %s(len: %d)\n",
msg, (int)strlen(msg));
} else {
fprintf(stdout, "Nothing sent due to an empty user input\n");
}
}
static void print_version(char *pname)
{
printf("%s %s\n", pname, git_version);
printf("%s %s\n", pname, zio_git_version);
}
int main(int argc, char *argv[])
{
int i, fd;
char adcfifo[128];
char msg[512], *ptr;
unsigned int devid;
if ((argc >= 2) && (!strcmp(argv[1], "-V"))) {
print_version(argv[0]);
exit(0);
}
if (argc < FALD_TRG_ARGC) {
fprintf(stderr, "\nUsage:\nfald-trg-cfg [-V] <dev_id> [options]\n");
return -1;
}
sscanf(argv[FALD_TRG_ARGC - 1], "%x", &devid);
if (access(adcfifo, F_OK) == -1) {
sprintf(adcfifo, "/tmp/adcfifo-%04x", devid);
/* create the FIFO (named pipe) */
mkfifo(adcfifo, 0666);
}
fd = open(adcfifo, O_WRONLY);
if (fd == -1) {
fprintf(stdout, "open %s failed errno:%d\n", adcfifo, errno);
exit(1);
}
/*
* If we have parameters from the command line, then send them
* immediately and close the program. This allow external program to
* invoke this one for the configuration instead of interactive input
*/
if (argc > FALD_TRG_ARGC) {
memset(msg, 0, 512);
for (i = 2; i < argc; ++i) {
strcat(msg, argv[i]);
strcat(msg, " ");
}
send_config(fd, msg);
close(fd);
return 0;
}
/* get user input */
while (1) {
memset(msg, 0, 512);
fprintf(stdout, "Change trig config using standard args: -a -b -c -n -e\n >>>: ");
ptr = fgets(msg, sizeof(msg), stdin);
if (!ptr) {
fprintf(stderr, "Error while reading options\n");
break;
}
send_config(fd, msg);
}
close(fd);
/* don't remove the FIFO to not break the reader side */
//unlink(adcfifo);
return 0;
}
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