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
The Driver Interface For Users
==============================
Installation
------------
This driver depends on two other drivers, as well as the Linux kernel.
Also, it must talk to a specific FPGA binary file running in the carrier
card.
Gateware
''''''''
The driver does not support all the gateware version; each gateware
main release need a new driver release which typically is not backward
compatible. So the latest version of the driver supports only the
latest version of the gateware
.. note:: On 2018-02 driver v5.0 supports gateware v5.0
On the ohwr `wiki page`_ you can find other details about all the past
releases.
.. _`wiki page`: http://www.ohwr.org/projects/fmc-adc-100m14b4cha-sw/wiki/Releases
To install the FPGA image in the target system, you need to place the
.bin file within ``/lib/firmware/``, where the system can find it.
The default gateware depends on the carrier in use. On a SVEC carrier
the default gateware is *fmc/spec-fmc-adc-100m14b.bin*; on a SPEC carrier
the default gateware is *fmc/spec-fmc-adc-100m14b.bin*
Anyway, you can use a different name for your gateware file and you can load
it by specifying the module parameter ``gateware=`` as described in the
`Module Parameters`_ section.
.. NOTE this can change in the future if we drop the fmc-bus
Software
''''''''
The kernel versions used during development are 3.2 and 3.6.
The driver is base on the `ZIO framework`_. The exact commit being used
for development is also used as a git submodule of this package, so it is
automatically built.
.. note:: On 2018-02 driver v5.0 used ZIO *zio-1.2-24-g0765fa5*
The driver is also based on the `fmc-bus`_. This bus manages FMC carriers
and mezzanines, identification and so on.
.. note:: On 2018-02 driver v5.0 used FMC-bus *fmc-bus-v2017-06*
.. note:: The fmc-bus support can be dropped in the future in favor of
a platform device
For the particular case of the SVEC carrier, in order to be able to use
the DMA operations, we need the header files of `SVEC`_ and the VME bus.
.. note:: On 2018-02 driver v5.0 used SVEC *svec-sw-v2014-04-hotfixes-44-gbdbc36c*
.. warning:: The VME bus used for this driver is the one developed at CERN
by the BE-CO-HT section. This is not publicly available, it means that
the SVEC support is limited to CERN users.
The versions mentioned above are the one used during the development. You may
want to use different version of those dependencis to include bugfixes or
improvements.
Now that you have the dependencies you can start building the driver.
The building process needs to know the location of the different dependencies.
``LINUX``
The top-level directory of the Linux kernel you are compiling
against. If not set, the default may work if you compile in the
same host where you expect to run the driver.
``ZIO``, ``FMC_BUS``
The top-level directory of the repository checkouts for the pacakges.
If unset, the top-level Makefile refers to the submodules of this package.
``CONFIG_FMC_ADC_SVEC``
It enables the `SVEC`_ support when its value is ``y``.
``SVEC_SW``, ``VMEBUS``
The top-level directory of the repository checkouts for the pacakges.
These are necessary only if you need the `SVEC`_ support.
When you have the necessary environment variables you can the ``make`` command::
make
sudo make install
.. _`ZIO framework`: http://www.ohwr.org/projects/zio
.. _`fmc-bus`: http://www.ohwr.org/projects/fmc-bus
.. _`SVEC`: https://www.ohwr.org/projects/svec-sw
.. _`SPEC`: https://www.ohwr.org/projects/spec-sw
Module Parameters
-----------------
The driver accepts a few load-time parameters for configuration. You can
pass them to insmod directly, or write them in ``/etc/modules.conf`` or
the proper file in ``/etc/modutils/``.
The following parameters are used:
gateware=PATH[, PATH]
The binary file to use to reprogram the FPGA. The default value for
this parameter is ``fmc/adc-100m14b.bin`` as seen in Gateware
Installation. The name is a relative pathname from
``/lib/firmware``, and it will be used for each and every card.
In combination with the busid, you can provide different file for
different card.
enable_test_data=[0, 1]
This is for testing purpose. When set to 1, this option enables the
testing data, so the ADC doesn't store samples, but fills memory with
sequential numbers. The 64 bit data vector is filled with sequential
values from a free-running 25 bit counter: channel 0 sweeps the full
range, channel 1 goes from 0 to 511, other channel always report 0.
Trigger detection is unaffected by use of test data.
busid=NUMBER[,NUMBER]
Restrict loading the driver to only a few mezzanine cards. If you
have several SPEC cards, most likely not all of them host an ADC
card; by specifying the list of bus identifiers you restrict the
module to only drive those cards. This option will remain, but is
going to be mostly obsoleted by use of eeprom-based identification
of the cards.
Device Abstraction
------------------
This driver is based on the ZIO framework and the fmc-bus. It supports
initial setup of the board; it allows users to manually configure the
board, to start and stop acquisitions, to force trigger, to read
acquisition time-stamps and to read acquired samples.
The driver is designed as a ZIO driver. ZIO is a framework for
input/output hosted on http://www.ohwr.org/projects/zio.
ZIO devices are organized as csets (channel sets), and each of them
includes channels. This device offers one cset and four channels.
However, the device can only stores interleaved data for all four
channels.
The current approach to this is defining 5 channels: channels 0 to 3 are
the actual input connectors, and their software counterpart is used to
configure the channels; the last channel is called *i*, and is the
interleave channel where data is retrieved.
The Overall Device
''''''''''''''''''
As said, the device has 1 cset with 4+1 channels. Channels from 0 to 3
represent che physical channels 1 to 4. The 5th channel *chani* represent
a virtual channel created automatically by the ZIO framework; this
channel represent the interleave acquisition on the cset.
.. graphviz::
:align: center
graph layers {
node [shape=box];
adc [label="FMC ADC 100M4B4CHA"];
adc -- cset0;
cset0 -- chan0;
cset0 -- chan1;
cset0 -- chan2;
cset0 -- chan3;
cset0 -- chani;
}
The ADC registers can be accessed in the proper sysfs directory. For a
card in slot 0 of bus 2 (like shown above), the directory is
*/sys/bus/zio/devices/adc-100m14b-0200*.
The overall device (*adc-100m14b*) does not offer configuration items
besides its own temperature (read-only) because configuration is
specific of the cset and the trigger, or the individual channel.
The Channel Set
'''''''''''''''
The ADC has 1 Channel Set named ``cset0``. Its attributes are used to
control the ADC state machine, the channel parameters and so on.
Some attributes are channel-specific, and one may thing they should live
at channel-level. Unfortunately, ZIO currently lacks the mechanisms to
convey channel attributes in the meta-data associated with an
interleaved acquisition (where several channels coexist), and for this
reason we chose to put them all at cset level. This may change in future
releases, but the library implementation will follow, so there will be
no effect on API users.
The description of attributes that follows is mainly useful for the
shell user, to diagnose the system and hack around with parameters.
Channel-specific Cset Attributes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The cset includes three attributes for each channel, as follows:
chN-50ohm-term
The read-write attribute accepts values 0 or 1. By writing 1, you
turn on the termination resistor. Default is 0.
chN-offset
The user offset is an integer value in the range [-5000,5000], and
it represents millivolts. The offset represents the center-scale
of conversion for the input channel. Internally, a DAC is used to
generate the requested voltage, which is then subtracted from the
input signal. DAC values are corrected according to the
calibration values retrieved from the FMC EEPROM. For this reason,
the offset may saturate at values less than +/- 5V.
chN-vref
The "voltage reference" used for conversion. This attribute may be
renamed to "range" in the future (again, with no effect on API
users). Changing the range does not reset the user offset, which
is automatically adjusted according to the new calibration values.
The attribute accepts three values: 35 represents the 100mV range
(-50mV to +50mV); 17 represents 1V range; 69 represents 10V range
(-5V to +5V); 0 detaches the input connector from the ADC. The
numbers used here derive from hardware values, and the attributes
refuses any other value.
cnN-saturation
The user saturation level in the range [0, 32767]. Users can use
this value to configure their own saturation level. The hardware
applies this value symmetrically on the negative side. By default
is setted at the maximum value.
Generic Cset Attributes
~~~~~~~~~~~~~~~~~~~~~~~
This section lists the attributes that are defined by this driver;
ZIO-wide attributes (current_buffer, enable and so on) are not
described.
fsm-auto-start
This attribute can be set to 1 or 0. It is 0 by default. If set
to 1, the acquisition state-machine is automatically restarted
after the previous run is complete. Thus, for example, a card
configured for external trigger, after the first acquisition will
continue aquiring and storing blocks to the ZIO buffer every time a
new trigger event is detected. Applications can read such blocks
from the char device.
fsm-command
Write-only: start (1) or stop (2) the state machine. The values
used reflects the hardware registers. Stopping the state machine
aborts any ongoing acquisition. Starting the state machine is
required in order to run an acquisition (the library manages this
internally). The green LED ACQ on the front panel reflect the fact
that the state machine has started. Restarting a running state
machine is equivalent to first stopping it.
fsm-state
Read-only current state of the FSM. Useful for diagnostics in
strange situation. Please refer to the firmware manual (or to
source code) about the various states.
resolution-bits
This read-only attribute returns 14, the number of valid bits in
the ADC data stream.
rst-ch-offset
This write-only attributes zeroes all offset DACs when written,
independently of the value being written. The driver applies the
current calibration values, instead of writing 0 directly to the
hardware.
undersample
The ADC always acquires at 100MSamples/s and this value cannot be
changed (it actually can, but it is not currently supported nor
even tested). If you need less samples you can tell the card to
decimate (or under-sample) the data stream. The attribute accepts
an integer value, 1 to 65536; it means to pick one sample every
that many. Thus, but writing 100 you get a 1Ms data stream, and by
writing 2 you get a 50Ms data stream.
sample-frequency
This read-only attributes returns the measured sampling frequency
sample-counter
Number of samples acquired on each channel during the last
acquisition. If queried while the acquisition is running you will
get the number of samples acquired till that moment. It can be
used to evaluate the progress of a slow acquisition.
max-sample-mshot
Maximum number of samples that can be stored in the FPGA memory in
multi-shot mode
Timestamp Cset Attributes
~~~~~~~~~~~~~~~~~~~~~~~~~
The ADC mark with a timestamp all these events: state machine start,
state machine stop and acquisition end. The device split each timestamp
in 3 attributes named: second (s), ticks (t) and bins (b).
Seconds represents (by default) the number of second since the epoch;
ticks is the number of clocks at 125Mhz, the value is between 0 and
125000000 and it increments seconds when it overflow. At the moment, the
bins register is unused.
For example, to read the entire timestamp of the state machine start
event you should do::
cat /sys/bus/zio/devices/adc-100m14b-0200/cset0/tstamp-acq-str-s
cat /sys/bus/zio/devices/adc-100m14b-0200/cset0/tstamp-acq-str-t
cat /sys/bus/zio/devices/adc-100m14b-0200/cset0/tstamp-acq-str-b
The driver export 4 time stamps:
tstamp-acq-str-{s|t|b}
this is the time stamp of the last acquisition start command
execution
tstamp-acq-end-{s|t|b}
it is the time of last sample acquired
tstamp-acq-stop-{s|t|b}
this is the time stamp of the last acquisition stop command
execution
tstamp-trg-lst-{s|t|b}
this is the time stamp of the last trigger fire. Please bear in
mind that in multi-shot acquisition you have several trigger fire,
so this time stamp refers only to the last one. If you need the
time stamp for each trigger fire you have to get it from the
zio_control of the associated acquisition block.
By default these time stamps represent (more or less) the time since the
epoch. The user can change this and configure a different timing base.
The attributes tstamp-base-s and tstamp-base-t are ment for this
purpose.
The Channels
''''''''''''
The ADC has 4 input channels. Each channel features one attribute, other
attributes in the directory are defined by the kernel or by ZIO.
current-value
the current value is a 16 bit number, resulting from the 14 bit ADC
value and calibration correction. The value is reported as unsigned,
even if it actually represents a signed 16-bit integer. (This because
ZIO manages 32-bit attributes and the value shown comes directly from
the hardware)
The Trigger
'''''''''''
In ZIO, the trigger is a separate software module, that can be replaced
at run time. This driver includes its own ZIO trigger type, that is
selected by default when the driver is initialized. You can change
trigger type (for example use the timer ZIO trigger) but this is not the
typical use case for this board.
The name of the ADC trigger is adc-100m14b. Like all other ZIO objects,
each instance of the trigger has a sysfs directory with its own
attributes:
The ADC has its own zio_trigger_type and it can not work with any other
ZIO's trigger. The ADC trigger is called fmc-adc-trg. We advise you
against replacing the trigger with another one.
The trigger supports four operating modes: the external trigger is
driven by a specific LEMO connector on the front panel of the card. The
internal trigger activates on data threshold in one of the four input
channels - either positive-going or negative-going. The timer trigger
that fires a trigger a given time. The software trigger is activated by
simply writing to a register.
This is the list of attributes (excluding kernel-generic and ZIO-generic
ones):
source-triggered
It is a bitmask where only one bit is set and it identifies the trigger
type that triggered the last acquisition. Look at the header file, or
the gateware document, for the meaning of each bit.
source
It is a bitmask that enable (1) or disable (0) the available triggers.
It supports multi-triggers, so you can enable more than one trigger at
the same time. Look at the header file, or the gateware document, for
the meaning of each bit.
polarity
It is a bitmask that set the trigger polarity to positive (0) on
negative (1) for each trigger that supports it. Look at the header file,
or the gateware document, for the meaning of each bit.
chN-threshold
These attributes choose the value of the data thresold (as a signed
16-bit value).
chN-hysteresis
These attributes choose the value of hysteresis associated to the
threshold.
chN-delay, ext-delay
The delay attribute tells how many samples to delay actual
acquisition since the trigger fired. Being sample-based, the
resolution is 10ns. By default delay is 0. The undersampling
does not have effect.
enable
This is a standard zio attribute, and the code uses it to enable or
disable the hardware trigger (i.e. internal and external). By
default the trigger is enabled.
int-channel, int-threshold
If the internal trigger is selected, these attributes choose the
channel being monitored (range is 0..3) and the value of the data
thresold (as a signed 16-bit value).
nshots
Number of trigger shots. The state machine acquires all trigger
events to internal on-board memory, and performs DMA only at the
end. In single-shot, the acquisition can be as long ad 32Msamples
(on-board memory is 256MB), but in multi-shot acquisition is first
done to in-FPGA memory, and thus each shot can only acquire 2048
samples.
post-samples, pre-samples
Number of samples to acquire. The pre-samples are acquired before
the actual trigger event (plus its optional delay). The post
samples start from the trigger-sample itself. The total number of
samples acquired corresponds to the sum of the two numbers. For
multi-shot acquisition, each shot acquires that many sample, but
pre + post must be at most 2048.
sw-trg-fire
To use the software trigger, you must first enable it (writing 1)
to sw-trg-enable. When enabled, by writing any values to
sw-trg-file you can force a trigger event. This is expected to be
used only for diagnostic reasons.
tstamp-trg-lst-b, tstamp-trg-lst-s, tstamp-trg-lst-t
To be verified and documented.
The Buffer
''''''''''
In ZIO, buffers are separate objects. The framework offers two buffer
types: kmalloc and vmalloc. The former uses the kmalloc function to
allocate each block, the latter uses vmalloc to allocate the whole data
area. While the kmalloc buffer is linked with the core ZIO kernel
module, vmalloc is a separate module. The driver currently prefers
kmalloc, but even when it preferred vmalloc (up to mid June 2013), if
the respective module wad not loaded, ZIO would instantiate kmalloc.
You can change the buffer type, while not acquiring, by writing its name
to the proper attribute. For example::
echo vmalloc > /sys/bus/zio/devices/adc-100m14b-0200/cset0/current_buffer
The disadvantage of kmalloc is that each block is limited in size.
usually 128kB (but current kernels allows up to 4MB blocks). The bigger
the block the more likely allocation fails. If you make a multi-shot
acquisition you need to ensure the buffer can fit enough blocks, and the
buffer size is defined for each buffer instance, i.e. for each channel.
In this case we acquire only from the interleaved channel, so before
making a 1000-long multishot acquisition you can do::
export DEV=/sys/bus/zio/devices/adc-100m14b-0200
echo 1000 > $DEV/cset0/chani/buffer/max-buffer-len
The vmalloc buffer allows mmap support, so when using vmalloc you can
save a copy of your data (actually, you save it automatically if you use
the library calls to allocate and fill the user-space buffer). However,
a vmalloc buffer allocates the whole data space at the beginning, which
may be unsuitable if you have several cards and acquire from one of them
at a time.
The vmalloc buffer type starts off with a size of 128kB, but you can
change it (while not aquiring), by writing to the associated attribute
of the interleaved channel. For example this sets it to 10MB::
export DEV=/sys/bus/zio/devices/adc-100m14b-0200
echo 10000 > $DEV/cset0/chani/buffer/max-buffer-kb
Summary of Attributes
'''''''''''''''''''''
The following table lists all attributes related to this driver. All
values are 32-bit that ZIO framework can handle only 32bit unsigned
integer.
.. list-table:: FMC ADC 100M 4B 4 C Attributes Summary
:header-rows: 1
:widths: 1 1 1 1 1 2
* - Context
- Name
- Permission
- Default
- Values
- Comments
* - device
- temperature
- ro
- --
-
- The temperature is in millidegree
* - cset
- enable
- rw
- 1
- [0, 1]
-
* - cset
- chN-50ohm-term
- rw
- 0
- [0, 1]
- N = 0..3
* - cset
- chN-offset
- rw
- 0
- [-5000; 5000]
- N = 0..3
* - cset
- chN-vref
- rw
- 17
- [0, 17, 35, 69]
- N = 0..3
* - cset
- chN-saturation
- rw
- 32767
- [0;32767]
- N = 0..3
* - cset
- fsm-auto-start
- rw
- 0
- [0, 1]
-
* - cset
- fsm-command
- wo
-
- [1, 2]
- 1: start, 2: stop
* - cset
- fsm-state
- ro
-
-
- hw values
* - cset
- max-sample-mshot
- ro
-
-
- hw value
* - cset
- resolution-bits
- ro
- 14
-
-
* - cset
- rst-ch-offset
- wo
-
- any
-
* - cset
- sample-decimation
- rw
- 1
-
-
* - cset
- sample-frequency
- ro
-
-
-
* - cset
- sample-counter
- ro
-
-
-
* - cset
- tstamp-acq-str-s
- ro
-
-
-
* - cset
- tstamp-acq-str-t
- ro
-
-
-
* - cset
- tstamp-acq-str-b
- ro
-
-
-
* - cset
- tstamp-acq-stp-s
- ro
-
-
-
* - cset
- tstamp-acq-stp-t
- ro
-
-
-
* - cset
- tstamp-acq-stp-b
- ro
-
-
-
* - cset
- tstamp-acq-end-s
- ro
-
-
-
* - cset
- tstamp-acq-end-t
- ro
-
-
-
* - cset
- tstamp-acq-end-b
- ro
-
-
-
* - chan
- current-value
- ro
-
-
-
* - trigger
- delay
- rw
- 0
- [0; ]
-
* - trigger
- enable
- rw
- 1
- [0, 1]
-
* - trigger
- external
- rw
- 1
- [0, 1]
-
* - trigger
- int-channel
- rw
- 0
- [0; 3]
-
* - trigger
- int-threshold
- rw
- 0
- [0; 65535]
- datum after offset/calibration
* - trigger
- nshots
- rw
- 1
- [0; 65535]
-
* - trigger
- polarity
- rw
- 0
- [0, 1]
- 0: raising, 1: falling
* - trigger
- post-samples
- rw
- 0
- Any
- max 2K if multishot
* - trigger
- pre-samples
- rw
- 0
- Any
- max 2K if multishot
* - trigger
- sw-trg-enable
- rw
- 0
- [0, 1]
-
* - trigger
- sw-trg-fire
- wo
- -
- Any
-
* - trigger
- tstamp-trg-s
- ro
-
-
-
* - trigger
- tstamp-trg-t
- ro
-
-
-
* - trigger
- tstamp-trg-b
- ro
-
-
-
Reading Data with Char Devices
------------------------------
To read data from user-space, applications should use the ZIO char
device interface. ZIO creates 2 char devices for each channel (as
documented in ZIO documentation). The ADC acquires only interleaved
samples, so ZIO creates two char device, as shown below::
$ ls -l /dev/zio/
total 0
crw------- 1 root root 250, 8 Aug 23 22:21 adc-100m14b-0200-0-i-ctrl
crw------- 1 root root 250, 9 Aug 23 22:21 adc-100m14b-0200-0-i-data
The actual pathnames depend on the version of udev you are running. The
fmc-adc library tries both names (the new one shown above, and the older
one, without a ``zio`` subdirectory). Also, please note that a still-newer
version of udev obeys device permissions, so you'll have read-only and
write-only device files (in this case they are both read-only).
If more than one board is probed for, you'll have two or more similar
pairs of devices, differing in the dev_id field, i.e. the ``0200`` shown
above. The dev_id field is built using the PCI bus and the devfn octet;
the example above refers to slot 0 of bus 2. (Most of the time each
PCI-E physical slot is mapped as a bus, so the slot number is usually
zero).
The ADC hardware does not allow to read data from a specific channel;
data is only transferred as an interleaved block of samples. Neither the
ZIO core nor the driver split interleaved data into 4 different buffers,
because that task is computationally intensive, and is better left to
the application (which may or may not need to do it). Thus, the driver
returns to user-space a block of interleaved samples.
To read this interleaved block you can read directly the interleaved
data char device adc-100m14b-0200-0-i-data using any program, for
example cat or hexdump::
$ hexdump -n 8 -e '"" 1/2 "%x\n"' /dev/zio/adc-100m14b-0200-0-i-data
fffc
e474
8034
8084
The ADC hardware always interleaves all 4 channels, and you cannot
acquire a subset of the channels. The acquired stream, thus, follows
this format:
.. figure:: img/interleaved.pdf
:alt: ADC interleaved data
The char-device model of ZIO is documented in the ZIO manual; basically,
the ctrl device returns metadata dna thr data device returns data. Items
in there are strictly ordered, so you can read metadata and then the
associated data, or read only data blocks and discard the associated
metadata.
The ``zio-dump`` tool, part of the ZIO distribution, turns metadata and data
into a meaningful grep-friendly text stream.
User Header Files
-----------------
Internally the driver uses the header file ``fmc-adc-100m14b4cha.h`` for the
declaration of all the functions, constants and structures. Some of these are
also available for the user-space programs; especially the constants to be
used to properly interpret the ``zio_control`` attributes, or the bitmask
fields definitions.
Troubleshooting
---------------
This chapter lists a few errors that may happen and how to deal with
them.
Installation issue with modules_install
'''''''''''''''''''''''''''''''''''''''
The command ``sudo make modules_install`` may place the modules in the wrong
directory or fail with an error like::
make: *** /lib/modules/<kernel-version>/build: No such file or directory.
This happens when you compiled by setting ``LINUX=`` and your sudo is not
propagating the environment to its child processes. In this case, you
should run this command instead::
sudo make modules_install LINUX=$LINUX
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;
}
/*
* ZIO-specific configuration (mostly 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 <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "fmcadc-lib.h"
#include "fmcadc-lib-int.h"
#define FMCADC_CONF_GET 0
#define FMCADC_CONF_SET 1
/*
* Internal functions to read and write a string.
* Trailing newlines are added/removed as needed
*/
static int __fa_zio_sysfs_set(struct __fmcadc_dev_zio *fa, char *name,
char *val, int maxlen)
{
char pathname[128];
char newval[maxlen + 1];
int fd, ret, len;
snprintf(pathname, sizeof(pathname), "%s/%s", fa->sysbase, name);
len = sprintf(newval, "%s\n", val);
fd = open(pathname, O_WRONLY);
if (fd < 0)
return -1;
ret = write(fd, newval, len);
close(fd);
if (ret < 0)
return -1;
if (ret == len)
return 0;
errno = EIO; /* short write */
return -1;
}
static int __fa_zio_sysfs_get(struct __fmcadc_dev_zio *fa, char *name,
char *val /* no maxlen: reader knows */ )
{
char pathname[128];
int fd, ret;
snprintf(pathname, sizeof(pathname), "%s/%s", fa->sysbase, name);
fd = open(pathname, O_RDONLY);
if (fd < 0)
return -1;
ret = read(fd, val, 128 /* well... user knows... */);
close(fd);
if (ret < 0)
return -1;
if (val[ret - 1] == '\n')
val[ret - 1] = '\0';
return 0;
}
/*
* Public functions (through ops and ./route.c).
* They manage both strings and integers
*/
int fmcadc_zio_set_param(struct fmcadc_dev *dev, char *name,
char *sptr, int *iptr)
{
struct __fmcadc_dev_zio *fa = to_dev_zio(dev);
char istr[12];
int len;
if (sptr)
return __fa_zio_sysfs_set(fa, name, sptr, strlen(sptr + 2));
len = sprintf(istr, "%i", *iptr);
return __fa_zio_sysfs_set(fa, name, istr, len + 2);
}
int fmcadc_zio_get_param(struct fmcadc_dev *dev, char *name,
char *sptr, int *iptr)
{
struct __fmcadc_dev_zio *fa = to_dev_zio(dev);
char istr[12];
int ret;
if (sptr)
return __fa_zio_sysfs_get(fa, name, sptr);
ret = __fa_zio_sysfs_get(fa, name, istr);
if (ret < 0)
return ret;
if (sscanf(istr, "%i", iptr) == 1)
return 0;
errno = EINVAL;
return -1;
}
/*
* Previous functions, now based on the public ones above
* Note: they are swapped: get, then set (above is set then get)
* FIXME: code using these must be refactored using data structures
*/
static int fa_zio_sysfs_get(struct __fmcadc_dev_zio *fa, char *name,
uint32_t *resp)
{
struct fmcadc_dev *dev = (struct fmcadc_dev *)&fa->gid; /* hack: back and forth.. */
int ret;
int val;
ret = fmcadc_zio_get_param(dev, name, NULL, &val);
if (!ret) {
*resp = val; /* different type */
return 0;
}
if (!(fa->flags & FMCADC_FLAG_VERBOSE))
return ret;
/* verbose tail */
if (ret)
fprintf(stderr, "lib-fmcadc: Error reading %s (%s)\n",
name, strerror(errno));
else
fprintf(stderr, "lib-fmcadc: %08x %5i <- %s\n",
(int)*resp, (int)*resp, name);
return ret;
}
int fa_zio_sysfs_set(struct __fmcadc_dev_zio *fa, char *name,
uint32_t *value)
{
struct fmcadc_dev *dev = (struct fmcadc_dev *)&fa->gid; /* hack: back and forth.. */
int ret;
int val = *value; /* different type */
ret = fmcadc_zio_set_param(dev, name, NULL, &val);
if (!ret)
return 0;
if (!(fa->flags & FMCADC_FLAG_VERBOSE))
return ret;
/* verbose tail */
if (ret)
fprintf(stderr, "lib-fmcadc: Error writing %s (%s)\n",
name, strerror(errno));
else
fprintf(stderr, "lib-fmcadc: %08x %5i -> %s\n",
(int)*value, (int)*value, name);
return ret;
}
static int fmcadc_zio_config_trg(struct __fmcadc_dev_zio *fa,
unsigned int index, uint32_t *value, unsigned int direction)
{
switch (index) {
case FMCADC_CONF_TRG_SOURCE:
if (direction)
return fa_zio_sysfs_set(fa, "cset0/trigger/external",
value);
else
return fa_zio_sysfs_get(fa, "cset0/trigger/external",
value);
break;
case FMCADC_CONF_TRG_SOURCE_CHAN:
if (direction)
return fa_zio_sysfs_set(fa, "cset0/trigger/int-channel",
value);
else
return fa_zio_sysfs_get(fa, "cset0/trigger/int-channel",
value);
break;
case FMCADC_CONF_TRG_THRESHOLD:
if (direction)
return fa_zio_sysfs_set(fa,
"cset0/trigger/int-threshold", value);
else
return fa_zio_sysfs_get(fa,
"cset0/trigger/int-threshold", value);
break;
case FMCADC_CONF_TRG_POLARITY:
if (direction)
return fa_zio_sysfs_set(fa, "cset0/trigger/polarity",
value);
else
return fa_zio_sysfs_get(fa, "cset0/trigger/polarity",
value);
break;
case FMCADC_CONF_TRG_DELAY:
if (direction)
return fa_zio_sysfs_set(fa, "cset0/trigger/delay",
value);
else
return fa_zio_sysfs_get(fa, "cset0/trigger/delay",
value);
break;
case FMCADC_CONF_TRG_THRESHOLD_FILTER:
if (direction)
return fa_zio_sysfs_set(fa, "cset0/trigger/int-threshold-filter",
value);
else
return fa_zio_sysfs_get(fa, "cset0/trigger/int-threshold-filter",
value);
break;
default:
errno = FMCADC_ENOCAP;
return -1;
}
}
static int fmcadc_zio_config_acq(struct __fmcadc_dev_zio *fa,
unsigned int index, uint32_t *value, unsigned int direction)
{
switch (index) {
case FMCADC_CONF_ACQ_N_SHOTS:
if (direction)
return fa_zio_sysfs_set(fa, "cset0/trigger/nshots",
value);
else
return fa_zio_sysfs_get(fa, "cset0/trigger/nshots",
value);
break;
case FMCADC_CONF_ACQ_POST_SAMP:
if (direction)
return fa_zio_sysfs_set(fa,
"cset0/trigger/post-samples", value);
else
return fa_zio_sysfs_get(fa,
"cset0/trigger/post-samples", value);
break;
case FMCADC_CONF_ACQ_PRE_SAMP:
if (direction)
return fa_zio_sysfs_set(fa, "cset0/trigger/pre-samples",
value);
else
return fa_zio_sysfs_get(fa, "cset0/trigger/pre-samples",
value);
break;
case FMCADC_CONF_ACQ_DECIMATION:
if (direction)
return fa_zio_sysfs_set(fa, "cset0/sample-decimation",
value);
else
return fa_zio_sysfs_get(fa, "cset0/sample-decimation",
value);
break;
case FMCADC_CONF_ACQ_FREQ_HZ:
if (direction) {
errno = FMCADC_ENOSET;
return -1;
} else {
*value = 100000000; /* 100Mhz */
return 0;
}
break;
case FMCADC_CONF_ACQ_N_BITS:
if (direction) {
errno = FMCADC_ENOSET;
return -1;
} else {
*value = 14;
return 0;
}
break;
default:
errno = FMCADC_ENOCAP;
return -1;
}
}
static int fmcadc_zio_config_chn(struct __fmcadc_dev_zio *fa, unsigned int ch,
unsigned int index, uint32_t *value, unsigned int direction)
{
char path[128];
switch (index) {
case FMCADC_CONF_CHN_RANGE:
sprintf(path, "cset%d/ch%d-vref", fa->cset, ch);
if (direction)
return fa_zio_sysfs_set(fa, path, value);
else
return fa_zio_sysfs_get(fa, path, value);
break;
case FMCADC_CONF_CHN_TERMINATION:
sprintf(path, "cset%d/ch%d-50ohm-term", fa->cset, ch);
if (direction)
return fa_zio_sysfs_set(fa, path, value);
else
return fa_zio_sysfs_get(fa, path, value);
break;
case FMCADC_CONF_CHN_OFFSET:
sprintf(path, "cset%d/ch%d-offset", fa->cset, ch);
if (direction)
return fa_zio_sysfs_set(fa, path, value);
else
return fa_zio_sysfs_get(fa, path, value);
break;
case FMCADC_CONF_CHN_SATURATION:
sprintf(path, "cset%d/ch%d-saturation", fa->cset, ch);
if (direction)
return fa_zio_sysfs_set(fa, path, value);
else
return fa_zio_sysfs_get(fa, path, value);
break;
default:
errno = FMCADC_ENOCAP;
return -1;
}
return 0;
}
static int fmcadc_zio_config_brd(struct __fmcadc_dev_zio *fa,
unsigned int index, uint32_t *value, unsigned int direction)
{
switch (index) {
case FMCADC_CONF_UTC_TIMING_BASE_S:
if (direction)
return fa_zio_sysfs_set(fa, "cset0/tstamp-base-s",
value);
else
return fa_zio_sysfs_get(fa, "cset0/tstamp-base-s",
value);
break;
case FMCADC_CONF_UTC_TIMING_BASE_T:
if (direction)
return fa_zio_sysfs_set(fa, "cset0/tstamp-base-t",
value);
else
return fa_zio_sysfs_get(fa, "cset0/tstamp-base-t",
value);
break;
case FMCADC_CONF_BRD_STATE_MACHINE_STATUS:
if (!direction)
return fa_zio_sysfs_get(fa, "cset0/fsm-state",
value);
errno = EINVAL;
return -1;
case FMCADC_CONF_BRD_N_CHAN:
if (!direction) {
*value = 4;
return 0;
}
errno = EINVAL;
return -1;
default:
errno = FMCADC_ENOCAP;
return -1;
}
}
static int fmcadc_zio_config(struct __fmcadc_dev_zio *fa, unsigned int flags,
struct fmcadc_conf *conf, unsigned int direction)
{
int err = 0, i;
uint32_t enabled;
/* Disabling the trigger before changing configuration */
if (direction) {
err = fa_zio_sysfs_get(fa, "cset0/trigger/enable", &enabled);
if (err)
return err;
if (enabled) {
enabled = 0;
err = fa_zio_sysfs_set(fa, "cset0/trigger/enable", &enabled);
/* restore the initial value */
enabled = 1;
}
}
for (i = 0; i < __FMCADC_CONF_LEN; ++i) {
if (!(conf->mask & (1LL << i)))
continue;
/* Parameter to configure */
switch (conf->type) {
case FMCADC_CONF_TYPE_TRG:
err = fmcadc_zio_config_trg(fa, i, &conf->value[i],
direction);
break;
case FMCADC_CONF_TYPE_ACQ:
err = fmcadc_zio_config_acq(fa, i, &conf->value[i],
direction);
break;
case FMCADC_CONF_TYPE_CHN:
if (conf->route_to > 3) {
errno = FMCADC_ENOCHAN;
return -1;
}
err = fmcadc_zio_config_chn(fa, conf->route_to,
i, &conf->value[i],
direction);
break;
case FMCADC_CONT_TYPE_BRD:
err = fmcadc_zio_config_brd(fa, i, &conf->value[i],
direction);
break;
default:
errno = FMCADC_ENOCFG;
return -1;
}
if (err)
break; /* stop the config process: an error occurs */
}
/* if the trigger was enabled restore it */
if (direction && enabled)
err = fa_zio_sysfs_set(fa, "cset0/trigger/enable", &enabled);
return err;
}
int fmcadc_zio_apply_config(struct fmcadc_dev *dev, unsigned int flags,
struct fmcadc_conf *conf)
{
struct __fmcadc_dev_zio *fa = to_dev_zio(dev);
return fmcadc_zio_config(fa, flags, conf, FMCADC_CONF_SET);
}
int fmcadc_zio_retrieve_config(struct fmcadc_dev *dev,
struct fmcadc_conf *conf)
{
struct __fmcadc_dev_zio *fa = to_dev_zio(dev);
return fmcadc_zio_config(fa, 0, conf, FMCADC_CONF_GET);
}
/*
* 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
/* Copyright 2013 CERN
* Author: Federico Vaga <federico.vaga@gmail.com>
* 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
*/
#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 <pthread.h>
#include <time.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/zio-user.h>
#include <fmcadc-lib.h>
#include <fmc-adc-100m14b4cha.h>
#ifdef DEBUG
#define fald_print_debug(format, ...) \
fprintf(stdout, "%s(%d)" format, __func__, __LINE__, ##__VA_ARGS__)
#else
#define fald_print_debug(format, ...)
#endif
static char git_version[] = "version: " GIT_VERSION;
static char zio_git_version[] = "zio version: " ZIO_GIT_VERSION;
static void fald_acq_stop(struct fmcadc_dev *adc, char *called_from);
static void fald_help()
{
printf("\nfald-simple-acq [OPTIONS] 0x<device-id>\n\n");
printf(" <device-id>: FMC identifier (e.g.: \"0x0400\")\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|-D <num> pick 1 sample every <num>\n");
printf(" --external|-e use external trigger\n");
printf(" --threshold|-t <num> internal trigger threshold\n");
printf(" --channel|-c <num> channel used as trigger (1..4)\n");
printf(" --range|-r <num> channel input range: "
"100(100mv) 1(1v) 10(10v)\n");
printf(" --sw-trigger|-w <num> ms to wait before generating "
"a software trigger\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(" --loop|-l <num> number of loop before exiting\n");
printf(" --show-data|-s <num> how many data to display: "
">0 from head, <0 from tail\n");
printf(" --graph|-g <chnum> plot the desired channel\n");
printf(" --X11|-X Gnuplot will use X connection\n");
printf(" --version|-V print version information\n");
printf(" --help|-h show this help\n\n");
}
static int trg_cfgval[__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'},
{"external", no_argument,
&trg_cfgval[FMCADC_CONF_TRG_SOURCE], 1},
{"threshold", required_argument, 0, 't'},
{"channel", required_argument, 0, 'c'},
{"timeout", required_argument, 0, 'T'},
{"negative-edge", no_argument,
&trg_cfgval[FMCADC_CONF_TRG_POLARITY], 1},
{"sw-trigger", required_argument, 0, 'w'},
/* new options, to help stress-test */
{"binary", required_argument, 0, 'B'},
{"multi-binary",required_argument, 0, 'M'},
{"dont-read", no_argument, 0, 'N'},
{"loop", required_argument, 0, 'l'},
{"show-data", required_argument, 0, 's'},
{"graph", required_argument, 0, 'g'},
{"X11 display", no_argument, 0, 'X'},
{"input-range", required_argument, 0, 'r'},
/* backward-compatible options */
{"pre", required_argument, 0, 'p'},
{"post", required_argument, 0, 'P'},
{"decimation", required_argument, 0, 'D'},
/* loop for stess test */
{"loop", required_argument, 0, 'l'},
{"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:N:l:s:r:g:X:p:P:D:Vhew:"
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);
}
/* variables shared between threads */
unsigned int devid = 0;
static pthread_mutex_t mtx;
static pthread_cond_t readyToPollCondVar;
static pthread_cond_t readyToReadOrCfgCondVar;
static int adc_wait_thread_ready;
static int adc_state;
static int poll_state;
static int new_config = 0;
static struct fmcadc_conf trg_cfg, acq_cfg, ch_cfg;
static int show_ndata = INT_MAX; /* by default all values are displayed */
static int plot_chno = -1;
static int x_display;
static int binmode;
static int timeout = -1;
static int loop = 1;
static char *basefile;
#define MAX_BUF 512
static char buf_fifo[MAX_BUF];
static char *_argv[16];
static int _argc;
static unsigned int sw_trigger_enable;
static unsigned int sw_trigger_enable_old;
static unsigned int sw_trigger_wait;
#define ADC_STATE_START_ACQ (1 << 0)
#define ADC_STATE_CHANGE_CFG (1 << 1)
#define ADC_STATE_FAILURE (1 << 2)
#define START_POLL 1
/* default is 1 V*/
static double bit_scale = 0.5/(1<<15);
/**
* It writes data on file
* @param[in] filename file where write data
* @param[in] ch the channel data to write
* @param[in] pdata data from ADC (is interleaved)
* @param[in] n_sample number of sample in pdata (is interleaved)
* @return 0 on success, otherwise -1 and errno is appropriately set
*/
static int write_file(const char *filename, int ch, int16_t *pdata,
unsigned int n_sample)
{
FILE *filp;
int i;
double val;
int mask = umask(0); /* set umask to have a file with 0666 mode */
filp = fopen(filename, "w");
umask(mask); /* restore previous umask */
if (filp == NULL) {
printf("fopen %s failed: %s", filename, strerror(errno));
return -1;
}
for (i = 0, pdata += (ch - 1); i < n_sample; ++i, pdata += 4) {
val = (*pdata)*bit_scale;
fprintf(filp, "%d %g\n", i, val);
}
if (fclose(filp) < 0)
return -1;
return 0;
}
/**
* It parses command line arguments and consequential it configures the device
* @param[in] argc number of arguments
* @param[in] argv arguments
*/
static void fald_acq_parse_args_and_configure(int argc, char *argv[])
{
int c, opt_index, val;
optind = 1; /* set to 1 to make getopt_long happy */
/* Parse options */
while ((c = getopt_long(argc, argv, GETOPT_STRING, options, &opt_index))
>= 0 ) {
switch (c) {
case 'b': case 'p': /* before */
fprintf(stdout, "FMCADC_CONF_ACQ_PRE_SAMP: %d\n",
atoi(optarg));
fmcadc_set_conf(&acq_cfg, FMCADC_CONF_ACQ_PRE_SAMP,
atoi(optarg));
break;
case 'a': case 'P': /* after */
fprintf(stdout, "FMCADC_CONF_ACQ_POST_SAMP: %d\n",
atoi(optarg));
fmcadc_set_conf(&acq_cfg, FMCADC_CONF_ACQ_POST_SAMP,
atoi(optarg));
break;
case 'n':
fprintf(stdout, "FMCADC_CONF_ACQ_N_SHOTS: %d\n",
atoi(optarg));
fmcadc_set_conf(&acq_cfg, FMCADC_CONF_ACQ_N_SHOTS,
atoi(optarg));
break;
case 'd':
fprintf(stdout, "FMCADC_CONF_TRG_DELAY: %d\n",
atoi(optarg));
fmcadc_set_conf(&trg_cfg, FMCADC_CONF_TRG_DELAY,
atoi(optarg));
break;
case 'u': case 'D':
fprintf(stdout, "FMCADC_CONF_ACQ_DECIMATION: %d\n",
atoi(optarg));
fmcadc_set_conf(&acq_cfg, FMCADC_CONF_ACQ_DECIMATION,
atoi(optarg));
break;
case 't':
fprintf(stdout, "FMCADC_CONF_TRG_THRESHOLD: %d\n",
atoi(optarg));
fmcadc_set_conf(&trg_cfg, FMCADC_CONF_TRG_THRESHOLD,
atoi(optarg));
break;
/*
* in-range
* 0x23 (35): 100mV range
* 0x11 (17): 1V range
* 0x45 (69): 10V range
* 0x00 (0): Open input
*/
case 'r':
val = atoi(optarg);
switch (val) {
case 100:
val = 0x23;
bit_scale = 0.05/(1<<15);
break;
case 1:
val = 0x11;
bit_scale = 0.5/(1<<15);
break;
case 10:
val = 0x45;
bit_scale = 5.0/(1<<15);
break;
}
fprintf(stdout, "FMCADC_CONF_CHN_RANGE: %d\n", val);
fmcadc_set_conf(&ch_cfg, FMCADC_CONF_CHN_RANGE,
val);
break;
case 'c':
val = atoi(optarg);
if (val < 1 || val > 4) {
fprintf(stderr, "Invalid channel %d\n", val);
fald_help();
exit(1);
}
fprintf(stdout, "FMCADC_CONF_TRG_SOURCE_CHAN: %d\n",
atoi(optarg));
/* set internal, and then the channel */
trg_cfgval[FMCADC_CONF_TRG_SOURCE] = 0; /* set later */
fmcadc_set_conf(&trg_cfg, FMCADC_CONF_TRG_SOURCE_CHAN,
val - 1);
break;
case 'e':
trg_cfgval[FMCADC_CONF_TRG_SOURCE] = 1;
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 'l':
loop = atoi(optarg);
break;
case 's':
show_ndata = atoi(optarg);
break;
case 'g':
plot_chno = atoi(optarg);
if (plot_chno < 1 || plot_chno > 4) {
fprintf(stderr, "Invalid channel %d\n",
plot_chno);
fald_help();
exit(1);
}
fprintf(stdout, "Plot channel %d\n", plot_chno);
break;
case 'X':
x_display = 1;
fprintf(stdout, "Gnuplot will use X display\n");
break;
case 'V':
print_version(argv[0]);
exit(0);
case 'w':
sw_trigger_enable = 1;
sw_trigger_wait = atoi(optarg);
break;
case 'h': case '?':
fald_help();
exit(1);
break;
}
}
/* Configure trigger (pick trigger polarity from external array) */
fmcadc_set_conf(&trg_cfg, FMCADC_CONF_TRG_POLARITY,
trg_cfgval[FMCADC_CONF_TRG_POLARITY]);
fmcadc_set_conf(&trg_cfg, FMCADC_CONF_TRG_SOURCE,
trg_cfgval[FMCADC_CONF_TRG_SOURCE]);
}
/**
* It applies the configuration for trigger, acquisition and channel
* @param[in] adc fmc-adc-100m device
* @param[in] trg_cfg trigger configuration status
* @param[in] acq_cfg acquisition configuration status
* @param[in] ch_cfg channel configuration status
* @return 0 on success, otherwise -1 and errno is appropriately set
*/
static void fald_acq_apply_config(struct fmcadc_dev *adc,
struct fmcadc_conf *trg_cfg,
struct fmcadc_conf *acq_cfg,
struct fmcadc_conf *ch_cfg)
{
int err;
err = fmcadc_apply_config(adc, 0 , trg_cfg);
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_cfg);
if (err && errno != FMCADC_ENOMASK) {
fprintf(stderr, "%s: cannot configure acquisition: %s\n",
_argv[0], fmcadc_strerror(errno));
exit(1);
}
/* Configure channel parameter */
err = fmcadc_apply_config(adc, 0 , ch_cfg);
if (err && errno != FMCADC_ENOMASK) {
fprintf(stderr, "%s: cannot configure channel0: %s\n",
_argv[0], fmcadc_strerror(errno));
exit(1);
}
// raise new_config flag
pthread_mutex_lock(&mtx);
new_config = 1;
pthread_mutex_unlock(&mtx);
}
/**
* It starts an acquisition
* @param[in] arg fmc-adc-100m device
* @param[in] called_from identifier of the parent function
* @param[in] flag flag for the fmcadc_acq_start() function
*/
static void fald_acq_start(struct fmcadc_dev *adc, char *called_from, int flag)
{
int try = 5, err;
struct timeval tv = {0, 0};
fald_print_debug("%s : call fmcadc_acq_start with %s\n",
called_from, ((flag) ? "flush" : "no flush"));
while (try) {
err = fmcadc_acq_start(adc, flag, &tv);
if (!err)
break;
/* Cannot start acquisition right now */
fprintf(stderr, "%s: cannot start acquisition: %s\n(Retry)\n",
_argv[0], fmcadc_strerror(errno));
/* Instead of leaving try another stop/start sequence */
fald_acq_stop(adc, "start_adc");
/* give a chance to breath in case the error persists */
sleep(1);
try--;
}
/*
* If also the last try fails, then set FAILURE state
* Otherwise, start polling
*/
if (!try) {
fald_print_debug("%s: Cannot start acquisition. Exit\n");
pthread_mutex_lock(&mtx);
adc_state |= ADC_STATE_FAILURE;
pthread_mutex_unlock(&mtx);
} else {
/* Start the poll */
pthread_mutex_lock(&mtx);
poll_state = START_POLL; /* adc has been flushed and started */
pthread_mutex_unlock(&mtx);
pthread_cond_signal(&readyToPollCondVar);
fald_print_debug("%s send signal to readyToPollCondVar\n", called_from);
}
}
/**
* It stops an acquisition
* @param[in] arg fmc-adc-100m device
* @param[in] called_from identifier of the parent function
*/
static void fald_acq_stop(struct fmcadc_dev *adc, char *called_from)
{
int try = 5, err;
/* stop any pending acquisition */
fald_print_debug("%s: call fmcadc_acq_stop\n", called_from);
while (try) {
err = fmcadc_acq_stop(adc, 0);
if (!err)
break;
fprintf(stderr, "%s: cannot stop acquisition: %s\n(Retry)\n",
_argv[0], fmcadc_strerror(errno));
try--;
}
/* If also the last try fails, then set FAILURE state */
if (!try) {
fald_print_debug("%s: Cannot stop acquisition. Exit\n");
pthread_mutex_lock(&mtx);
adc_state |= ADC_STATE_FAILURE;
pthread_mutex_unlock(&mtx);
}
}
static void adc_trigger_sw(struct fmcadc_dev *adc, unsigned int sdelay)
{
sleep(sdelay);
fmcadc_trigger_sw_fire(adc);
}
/**
* It waits until data is ready, then it send a signal and wait again for the
* next block of data
* @param[in] arg pointer to fmc-adc-100m device
*/
static void *adc_wait_thread(void *arg)
{
struct fmcadc_dev *adc = arg;
int err;
for (;;) {
pthread_mutex_lock(&mtx);
while (!poll_state) {
fald_print_debug("cond_wait readyToPollCondVar\n");
adc_wait_thread_ready = 1;
pthread_cond_wait(&readyToPollCondVar, &mtx);
}
poll_state = 0;
pthread_mutex_unlock(&mtx);
if (sw_trigger_enable)
adc_trigger_sw(adc, sw_trigger_wait);
fald_print_debug("It's time to call fmcadc_acq_poll\n");
err = fmcadc_acq_poll(adc, 0 , NULL);
if (err) {
if (errno == FMCADC_EDISABLED) {
fprintf(stderr, "fmcadc_acq_poll has been aborted due to a triiger stop: err:%d errno:%s(%d)\n",
err, fmcadc_strerror(errno), errno);
continue;
} else {
fprintf(stderr, "fmcadc_acq_poll failed: err:%d errno:%s(%d)\n",
err, strerror(errno), errno);
exit(-1);
}
}
fald_print_debug("fmc-adc_poll ends normally, send signal to readyToReadOrCfgCondVar requesting to read data\n");
pthread_mutex_lock(&mtx);
adc_state |= ADC_STATE_START_ACQ; /* means start acquisition */
pthread_mutex_unlock(&mtx);
// Wake up the change config thread
pthread_cond_signal(&readyToReadOrCfgCondVar);
}
}
/**
* It configures the device. It applies configurations coming from a temporary
* files. Other process may write configurations on this file.
* @param[in] arg pointer to fmc-adc-100m device
*/
static void *change_config_thread(void *arg)
{
struct fmcadc_dev *adc = arg;
int fd, ret;
char adcfifo[128];
char *s, *t;
char buf[MAX_BUF];
if (access(adcfifo, F_OK) == -1) {
sprintf(adcfifo, "/tmp/adcfifo-%04x", devid);
/* create the FIFO (named pipe) */
mkfifo(adcfifo, 0666);
}
/* open, read, and display the message from the FIFO */
fd = open(adcfifo, O_RDONLY);
for (;;) {
memset(buf, 0, MAX_BUF);
ret = read(fd, buf, MAX_BUF);
if (ret > 0) {
_argc = 1;
memcpy(buf_fifo, buf, MAX_BUF); /* for future parsing */
s = buf_fifo;
while ((t = strtok(s, " ")) != NULL) {
s = NULL;
_argv[_argc++] = t;
}
/* async way of changing trig config */
fald_acq_stop(adc, "change_config");
fald_acq_parse_args_and_configure(_argc, _argv);
fald_acq_apply_config(adc, &trg_cfg, &acq_cfg, &ch_cfg);
fprintf(stdout, "mainThread: Change trig config ................. done\n");
fald_acq_start(adc, "change_config", FMCADC_F_FLUSH) ;
} else {
fprintf(stdout, "read returns %d\n", ret);
/* writer close the fifo. Colse the reader side */
close(fd);
/* should block until the writer is back */
fd = open(adcfifo, O_RDONLY);
}
}
/* function never returns, but return NULL to avoid warning */
return NULL;
}
/**
* It creates threads for configuration and to wait data. We do not really
* need these threads but we did it on purpose in order to emulate a complex
* scenario and stress the library/driver.
* @param[in] adc fmc-adc-100m device
*/
static void create_thread(struct fmcadc_dev *adc)
{
/* Config thread */
pthread_attr_t thread_attr;
pthread_t tid;
pthread_attr_init(&thread_attr);
pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
int res = pthread_create(&tid, &thread_attr, change_config_thread, (void *)adc);
if (res)
fprintf(stderr, "Cannot create 'change_config_thread' (%d)\n",
res);
pthread_attr_init(&thread_attr);
pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
res = pthread_create(&tid, &thread_attr, adc_wait_thread, (void *)adc);
if (res)
fprintf(stderr, "Cannot create 'adc_wait_thread' (%d)\n", res);
// initialize condition variables
pthread_condattr_t condVarAttr;
pthread_condattr_init(&condVarAttr);
pthread_cond_init(&readyToPollCondVar, &condVarAttr);
pthread_condattr_destroy(&condVarAttr);
pthread_condattr_init(&condVarAttr);
pthread_cond_init(&readyToReadOrCfgCondVar, &condVarAttr);
pthread_condattr_destroy(&condVarAttr);
// initialize mutexes
pthread_mutexattr_t mtxAttr;
pthread_mutexattr_init(&mtxAttr);
pthread_mutex_init(&mtx, &mtxAttr);
pthread_mutexattr_destroy(&mtxAttr);
/* Give a chance to newly created thread to be up and running */
// sleep(1);
}
/**
* It retreive the current configuration of the library/driver
* @param[in] adc fmc-adc-100m device
* @param[out] trg_cfg trigger configuration status
* @param[out] acq_cfg acquisition configuration status
* @param[out] ch_cfg channel configuration status
* @return 0 on success, otherwise -1 and errno is appropriately set
*/
static int fald_acq_get_configuration(struct fmcadc_dev *adc,
struct fmcadc_conf *trg_cfg,
struct fmcadc_conf *acq_cfg,
struct fmcadc_conf *ch_cfg)
{
int err;
/* Retreive trigger configuration */
memset(trg_cfg, 0, sizeof(struct fmcadc_conf));
trg_cfg->type = FMCADC_CONF_TYPE_TRG;
fmcadc_set_conf_mask(trg_cfg, FMCADC_CONF_TRG_SOURCE);
fmcadc_set_conf_mask(trg_cfg, FMCADC_CONF_TRG_SOURCE_CHAN);
fmcadc_set_conf_mask(trg_cfg, FMCADC_CONF_TRG_THRESHOLD);
fmcadc_set_conf_mask(trg_cfg, FMCADC_CONF_TRG_POLARITY);
fmcadc_set_conf_mask(trg_cfg, FMCADC_CONF_TRG_DELAY);
err = fmcadc_retrieve_config(adc, trg_cfg);
if (err) {
fprintf(stderr, "Cannot get trigger config: %s\n",
fmcadc_strerror(errno));
return -1;
}
/* Retreive acquisition configuration */
memset(acq_cfg, 0, sizeof(struct fmcadc_conf));
acq_cfg->type = FMCADC_CONF_TYPE_ACQ;
fmcadc_set_conf_mask(acq_cfg, FMCADC_CONF_ACQ_N_SHOTS);
fmcadc_set_conf_mask(acq_cfg, FMCADC_CONF_ACQ_POST_SAMP);
fmcadc_set_conf_mask(acq_cfg, FMCADC_CONF_ACQ_PRE_SAMP);
fmcadc_set_conf_mask(acq_cfg, FMCADC_CONF_ACQ_DECIMATION);
err = fmcadc_retrieve_config(adc, acq_cfg);
if (err) {
fprintf(stderr, "Cannot get acquisition config: %s\n",
fmcadc_strerror(errno));
return -1;
}
/* Retreive channel configuration */
memset(ch_cfg, 0, sizeof(struct fmcadc_conf));
ch_cfg->type = FMCADC_CONF_TYPE_CHN;
ch_cfg->route_to = 0; /* channel 0 */
fmcadc_set_conf_mask(ch_cfg, FMCADC_CONF_CHN_RANGE);
fmcadc_set_conf_mask(ch_cfg, FMCADC_CONF_CHN_TERMINATION);
fmcadc_set_conf_mask(ch_cfg, FMCADC_CONF_CHN_OFFSET);
err = fmcadc_retrieve_config(adc, ch_cfg);
if (err) {
fprintf(stderr, "Cannot get channel config: %s\n",
fmcadc_strerror(errno));
return -1;
}
return 0;
}
/**
* It prints data to stdout
* @param[in] ctrl
* @param[in] buf buffer to print
* @param[in] acq_cfg acquisition configuration associated to the buffer
*/
static void fald_acq_print_data(struct fmcadc_buffer *buf,
struct fmcadc_conf *acq_cfg,
unsigned int n)
{
struct zio_control *ctrl;
int j, ch;
int16_t *data; /* FMC-ADC-100M sample size is 14bit, 16bit for ZIO */
if (n == 0)
return;
ctrl = buf->metadata;
if (ctrl->nsamples / 4 != buf->nsamples) {
fprintf(stdout, "discrepancy between ctrl->nsamples: %d and buf->nsamples: %d\n",
ctrl->nsamples, buf->nsamples);
return;
}
data = buf->data;
/* Print data */
for (j = 0; j < ctrl->nsamples / 4; j++) {
if ( (n > 0 && j < n) ||
(n < 0 && (ctrl->nsamples / 4 - j) <= (-n)) ) {
printf("%5i ", j - acq_cfg->value[FMCADC_CONF_ACQ_PRE_SAMP]);
for (ch = 0; ch < 4; ch++)
printf("%7i", *(data++));
printf("\n");
} else {
data += 4;
}
}
}
/**
* Save data to a single file
* @param[in] buf buffer to store
* @return 0 on success, otherwise -1 and errno is appropriately set
*/
static int fald_acq_write_single(struct fmcadc_buffer *buf)
{
struct zio_control *ctrl;
char fname[PATH_MAX];
int16_t *data; /* FMC-ADC-100M sample size is 14bit, 16bit for ZIO */
FILE *f;
int err = 0;
sprintf(fname, "%s", basefile);
f = fopen(fname, "a");
if (!f) {
fprintf(stderr, "%s: %s\n",
fname, strerror(errno));
exit(1);
}
ctrl = buf->metadata;
if (fwrite(ctrl, sizeof(struct zio_control), 1, f) != 1)
err++;
data = buf->data;
if (fwrite(data, ctrl->ssize, ctrl->nsamples, f)
!= ctrl->nsamples)
err++;
if (err) {
fprintf(stderr, "write(%s): short write\n",
basefile);
return -1;
}
return 0;
}
/**
* Sava data to different file for control and data for each shot
* @param[in] buf buffer to store
* @param[in] shot_i i-th shot acquisition
* @return 0 on success, otherwise -1 and errno is appropriately set
*/
static int fald_acq_write_multiple(struct fmcadc_buffer *buf,
unsigned int shot_i)
{
char fname[PATH_MAX];
struct zio_control *ctrl;
uint16_t *data;
FILE *f;
sprintf(fname, "%s.%03i.ctrl", basefile, shot_i);
f = fopen(fname, "w");
if (!f) {
fprintf(stderr, "%s: %s\n",
fname, strerror(errno));
return -1;
}
ctrl = buf->metadata;
if (fwrite(ctrl, sizeof(struct zio_control), 1, f) != 1) {
fprintf(stderr, "write(%s): short write\n", fname);
return -1;
}
fclose(f);
sprintf(fname, "%s.%03i.data", basefile, shot_i);
f = fopen(fname, "w");
if (!f) {
fprintf(stderr, "%s: %s\n",
fname, strerror(errno));
return -1;
}
data = buf->data;
if (fwrite(data, ctrl->ssize, ctrl->nsamples, f)
!= ctrl->nsamples) {
fprintf(stderr, "write(%s): short write\n", fname);
return -1;
}
fclose(f);
return 0;
}
/**
* Plot data using gnuplot
* @param[in] buf buffer to plot
* @param[in] ch channel to show
* @return 0 on success, otherwise -1 and errno is appropriately set
*/
static void fald_acq_plot_data(struct fmcadc_buffer *buf, unsigned int ch)
{
struct zio_control *ctrl = buf->metadata;
int16_t *data = buf->data;
char fname[PATH_MAX];
char cmd[256];
snprintf(fname, sizeof(fname), "/tmp/fmcadc.0x%04x.ch%d.dat", devid, ch);
if (write_file(fname, ch, data, (ctrl->nsamples)/4) < 0) {
printf("Cannot plot data. Write data into file %s failed.\n", fname);
return;
}
snprintf(cmd, sizeof(cmd), "echo \"%s plot '%s' with lines "
"\" | gnuplot -persist",
(x_display == 1 ? "set term x11; " : "set term dumb; "),
fname);
cmd[sizeof(cmd) - 1] = '\0';
system(cmd);
}
/**
* It handles a a shot. Retreive data from the driver and show data
*/
static int fald_acq_handle_shot(struct fmcadc_dev *adc,
struct fmcadc_conf *acq_cfg,
struct fmcadc_buffer *buf,
unsigned int shot_i)
{
struct zio_control *ctrl;
int err;
if (binmode < 0) /* no data must be acquired */
return -1;
err = fmcadc_fill_buffer(adc, buf, 0, NULL);
if (err) {
if (errno == FMCADC_EDISABLED) {
fprintf(stdout, "mainThread: leaves fmcadc_fill_buffer with errno=FMCADC_EDISABLED\n");
return 0;
}
fprintf(stderr, "shot %i/%i: cannot fill buffer: %s\n", shot_i + 1,
acq_cfg->value[FMCADC_CONF_ACQ_N_SHOTS],
fmcadc_strerror(errno));
return -1;
}
ctrl = buf->metadata;
/* FIXME adc-lib should provide enums to retrive
* attributes values */
fprintf(stderr, "Acquisition started at secs:%u ticks:%u\n",
ctrl->attr_channel.ext_val[FA100M14B4C_DATTR_ACQ_START_S],
ctrl->attr_channel.ext_val[FA100M14B4C_DATTR_ACQ_START_C]);
fprintf(stderr, "Read %d samples from shot %i/%i secs:%lld ticks:%lld (loop: %d)\n",
ctrl->nsamples,
shot_i + 1, acq_cfg->value[FMCADC_CONF_ACQ_N_SHOTS],
(long long)ctrl->tstamp.secs, (long long)ctrl->tstamp.ticks, loop);
/* print/store data*/
switch(binmode) {
default:
fald_acq_print_data(buf, acq_cfg, show_ndata);
break;
case 1:
err = fald_acq_write_single(buf);
break;
case 2:
err = fald_acq_write_multiple(buf, shot_i);
break;
}
if (err)
return -1;
return 0;
}
int main(int argc, char *argv[])
{
struct fmcadc_dev *adc;
struct fmcadc_buffer *buf;
int i, err;
if (argc == 1) {
fprintf(stderr, "%s: DEVICE-ID is a mandatory argument\n",
argv[0]);
fald_help();
exit(1);
}
/* parsing of arguments is done after fmcadc_open, so we have to check
* here whether we should print help */
if ((argc >= 2) && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) {
fald_help();
exit(1);
}
/* parsing of arguments is done after fmcadc_open, so we have to check
* here whether we should print version */
if ((argc >= 2) &&
(!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version"))) {
print_version(argv[0]);
exit(0);
}
/* set local _argv[0] with pg name */
_argv[0] = argv[0];
/* devid is the last arg */
sscanf(argv[argc-1], "%x", &devid);
/* Open the ADC */
adc = fmcadc_open("fmc-adc-100m14b4cha", devid,
/* nshots * (presamples + postsamples) */
/*
acq.value[FMCADC_CONF_ACQ_N_SHOTS] *
( acq.value[FMCADC_CONF_ACQ_PRE_SAMP] +
acq.value[FMCADC_CONF_ACQ_POST_SAMP] )*/ 0,
/*acq.value[FMCADC_CONF_ACQ_N_SHOTS]*/ 0,
FMCADC_F_FLUSH /*0*/);
if (!adc) {
fprintf(stderr, "%s: cannot open device: %s\n",
argv[0], fmcadc_strerror(errno));
exit(1);
}
fmcadc_trigger_sw_status(adc, &sw_trigger_enable_old);
if (sw_trigger_enable) {
fmcadc_trigger_sw_enable(adc, sw_trigger_enable);
}
/* Before parsing args : */
/* First retrieve current config in case the program */
/* is launched with a subset of options */
err = fald_acq_get_configuration(adc, &trg_cfg, &acq_cfg, &ch_cfg);
if (err) {
exit(1);
}
/* get the new given trigger and acq config */
/* Only the ones provided will override the current ones */
fald_acq_parse_args_and_configure(argc, argv);
/* fmc-adc-100m work only with ZIO framework */
if (strcmp(fmcadc_get_driver_type(adc), "zio")) {
fprintf(stderr, "%s: not a zio driver, aborting\n", argv[0]);
exit(1);
}
/* create the various thread and sync mechanism */
create_thread(adc);
while (!adc_wait_thread_ready)
nanosleep((struct timespec[]){{0, 10000}}, NULL);
fprintf(stdout, "adc_wait_thread_ready\n");
/* configure adc */
fald_acq_stop(adc, "main");
fald_acq_apply_config(adc, &trg_cfg, &acq_cfg, &ch_cfg);
/* Allocate a first buffer in the default way */
buf = fmcadc_request_buffer(adc,
acq_cfg.value[FMCADC_CONF_ACQ_PRE_SAMP] +
acq_cfg.value[FMCADC_CONF_ACQ_POST_SAMP],
NULL /* alloc */, 0);
if (!buf) {
fprintf(stderr, "Cannot allocate buffer (%s)\n",
fmcadc_strerror(errno));
exit(1);
}
fald_acq_start(adc, "main", FMCADC_F_FLUSH);
while (loop > 0) {
pthread_mutex_lock(&mtx);
while (!adc_state) {
fald_print_debug("mainThread: cond_wait readyToReadOrCfgCondVar\n");
pthread_cond_wait(&readyToReadOrCfgCondVar, &mtx);
fald_print_debug("mainThread: waked up adc_state:%d\n", adc_state);
}
fald_print_debug("mainThread: wakeup readyToReadOrCfgCondVar with adc_state: %s\n",
((adc_state==1)?"Read data":"Change trigger config"));
/* If the system fails stop the loop and close the program */
if (adc_state & ADC_STATE_FAILURE) {
pthread_mutex_unlock(&mtx);
break;
}
/* time to acquire data */
adc_state &= ~ADC_STATE_START_ACQ; /* ack time to acquire data */
if (new_config && buf != NULL) {
/* first release previous buffer */
fmcadc_release_buffer(adc, buf, NULL);
buf = NULL;
new_config = 0;
}
pthread_mutex_unlock(&mtx);
if (buf == NULL) { /* buf has been released due to a change of trig config */
/* Allocate a buffer in the default way */
buf = fmcadc_request_buffer(adc,
acq_cfg.value[FMCADC_CONF_ACQ_PRE_SAMP] +
acq_cfg.value[FMCADC_CONF_ACQ_POST_SAMP],
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_cfg.value[FMCADC_CONF_ACQ_N_SHOTS]; ++i) {
err = fald_acq_handle_shot(adc, &acq_cfg, buf, i);
if (err)
exit(1);
}
--loop;
if (loop > 0)
fald_acq_start(adc, "End of data processing", 0);
else if (plot_chno != -1) /* Plot only the last Acquisition */
fald_acq_plot_data(buf, plot_chno);
}
fmcadc_trigger_sw_enable(adc, sw_trigger_enable_old);
fmcadc_close(adc);
exit(0);
}
/*
* 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);
}
/* 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 <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-get-conf [OPTIONS] 0x<DEVICE ID>\n\n");
printf(" <DEVICE>: hexadecimal string which represent the device "
"identificator of an fmc-adc\n");
printf(" --version|-V: show version information\n");
printf(" --help|-h: show this help\n\n");
}
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 trg, acq, brd, chn;
static struct option options[] = {
{"version", no_argument, 0, 'V'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
int opt_index = 0, err = 0, i;
unsigned int dev_id = 0;
char c;
if (argc == 1) {
fald_help();
exit(1);
}
/* reset attribute's mask */
trg.mask = 0;
acq.mask = 0;
/* Parse options */
while((c = getopt_long(argc, argv, "hV", options, &opt_index)) >= 0) {
switch (c) {
case 'V':
print_version(argv[0]);
exit(0);
case 'h':
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], "0x%x", &dev_id);
}
printf("Open ADC fmc-adc-100m14b4cha dev_id 0x%04x ...\n", dev_id);
/* Open the ADC */
adc = fmcadc_open("fmc-adc-100m14b4cha", dev_id,
/* no buffers expexted */ 0, 0,
/* no flush either */ 0);
if (!adc) {
fprintf(stderr, "%s: cannot open device: %s\n",
argv[0], fmcadc_strerror(errno));
exit(1);
}
printf("Get Trigger Configuration ...\n");
/* Configure Trigger parameter to retrieve */
trg.type = FMCADC_CONF_TYPE_TRG;
fmcadc_set_conf_mask(&trg, FMCADC_CONF_TRG_SOURCE);
fmcadc_set_conf_mask(&trg, FMCADC_CONF_TRG_SOURCE_CHAN);
fmcadc_set_conf_mask(&trg, FMCADC_CONF_TRG_THRESHOLD);
fmcadc_set_conf_mask(&trg, FMCADC_CONF_TRG_POLARITY);
fmcadc_set_conf_mask(&trg, FMCADC_CONF_TRG_DELAY);
err = fmcadc_retrieve_config(adc, &trg);
if (err) {
fprintf(stderr, "%s: cannot get trigger config: %s\n",
argv[0], fmcadc_strerror(errno));
exit(1);
}
printf(" source: %s\n",
trg.value[FMCADC_CONF_TRG_SOURCE] ? "external" : "internal");
printf(" channel: %d\n", trg.value[FMCADC_CONF_TRG_SOURCE_CHAN]);
printf(" threshold: %d\n", trg.value[FMCADC_CONF_TRG_THRESHOLD]);
printf(" polarity: %d\n", trg.value[FMCADC_CONF_TRG_POLARITY]);
printf(" delay: %d\n", trg.value[FMCADC_CONF_TRG_DELAY]);
printf("Get Acquisition Configuration ...\n");
/* Configure acquisition parameter */
acq.type = FMCADC_CONF_TYPE_ACQ;
fmcadc_set_conf_mask(&acq, FMCADC_CONF_ACQ_N_SHOTS);
fmcadc_set_conf_mask(&acq, FMCADC_CONF_ACQ_POST_SAMP);
fmcadc_set_conf_mask(&acq, FMCADC_CONF_ACQ_PRE_SAMP);
fmcadc_set_conf_mask(&acq, FMCADC_CONF_ACQ_DECIMATION);
fmcadc_set_conf_mask(&acq, FMCADC_CONF_ACQ_FREQ_HZ);
fmcadc_set_conf_mask(&acq, FMCADC_CONF_ACQ_N_BITS);
err = fmcadc_retrieve_config(adc, &acq);
if (err) {
fprintf(stderr, "%s: cannot get acquisition config: %s\n",
argv[0], fmcadc_strerror(errno));
exit(1);
}
printf(" n-shots: %d\n", acq.value[FMCADC_CONF_ACQ_N_SHOTS]);
printf(" post-sample: %d\n", acq.value[FMCADC_CONF_ACQ_POST_SAMP]);
printf(" pre-sample: %d\n", acq.value[FMCADC_CONF_ACQ_PRE_SAMP]);
printf(" decimation: %d\n", acq.value[FMCADC_CONF_ACQ_DECIMATION]);
printf(" frequency: %dHz\n", acq.value[FMCADC_CONF_ACQ_FREQ_HZ]);
printf(" n-bits: %d\n", acq.value[FMCADC_CONF_ACQ_N_BITS]);
printf("Get Board Configuration ...\n");
/* Configure acquisition parameter */
brd.type = FMCADC_CONT_TYPE_BRD;
fmcadc_set_conf_mask(&brd, FMCADC_CONF_BRD_STATE_MACHINE_STATUS);
fmcadc_set_conf_mask(&brd, FMCADC_CONF_BRD_N_CHAN);
err = fmcadc_retrieve_config(adc, &brd);
if (err) {
fprintf(stderr, "%s: cannot get board config: %s\n",
argv[0], fmcadc_strerror(errno));
exit(1);
}
printf(" n-chan: %d\n", brd.value[FMCADC_CONF_BRD_N_CHAN]);
printf(" State Machine: %d\n",
brd.value[FMCADC_CONF_BRD_STATE_MACHINE_STATUS]);
for (i = 0; i < brd.value[FMCADC_CONF_BRD_N_CHAN]; ++i) {
printf("Get Channel %d Configuration ...\n", i);
/* Configure acquisition parameter */
chn.type = FMCADC_CONF_TYPE_CHN;
chn.route_to = i;
fmcadc_set_conf_mask(&chn, FMCADC_CONF_CHN_RANGE);
fmcadc_set_conf_mask(&chn, FMCADC_CONF_CHN_TERMINATION);
fmcadc_set_conf_mask(&chn, FMCADC_CONF_CHN_OFFSET);
err = fmcadc_retrieve_config(adc, &chn);
if (err) {
fprintf(stderr, "%s: cannot get channel config: %s\n",
argv[0], fmcadc_strerror(errno));
exit(1);
}
printf(" range: %d\n", chn.value[FMCADC_CONF_CHN_RANGE]);
printf(" 50Ohm Termination: %s\n",
chn.value[FMCADC_CONF_CHN_TERMINATION] ? "yes" : "no");
printf(" offset: %d\n", chn.value[FMCADC_CONF_CHN_OFFSET]);
}
fmcadc_close(adc);
exit(0);
}
/* Copyright 2013 CERN
* Author: Federico Vaga <federico.vaga@gmail.com>
* License: GPLv2
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <getopt.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
#include <errno.h>
#include <linux/zio-user.h>
#include <fmcadc-lib.h>
/* Global configuration*/
#define N_CHAN 4
#define CARD_NAME "fmc-adc-100m14b4cha"
static char git_version[] = "version: " GIT_VERSION;
static char zio_git_version[] = "zio version: " ZIO_GIT_VERSION;
unsigned nshots = 1;
unsigned presamples = 10;
unsigned postsamples = 10;
int print_data = 1;
void print_buffer_content(struct fmcadc_buffer *buf);
int read_with_one_buffer(struct fmcadc_dev *dev);
int read_with_n_buffer(struct fmcadc_dev *dev);
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[])
{
unsigned long totalsamples = nshots * (presamples + postsamples);
unsigned int dev_id, n_buffers = nshots;
struct fmcadc_conf acq, trg;
struct fmcadc_dev *dev;
int test = 0, err;
if ((argc >= 2) && (!strcmp(argv[1], "-V"))) {
print_version(argv[0]);
exit(0);
}
if (argc != 3) {
fprintf(stderr, "%s: Use \"%s [-V] <dev_id> <testnr>\n",
argv[0], argv[0]);
exit(1);
}
sscanf(argv[1], "%x", &dev_id);
sscanf(argv[2], "%i", &test);
/* Change parameters from environment */
if (getenv("FALD_TEST_NSHOTS"))
nshots = atoi(getenv("FALD_TEST_NSHOTS"));
if (getenv("FALD_TEST_PRE_S"))
presamples = atoi(getenv("FALD_TEST_PRE_S"));
if (getenv("FALD_TEST_POST_S"))
postsamples = atoi(getenv("FALD_TEST_POST_S"));
if (getenv("FALD_TEST_NOPRINT"))
print_data = 0;
fmcadc_init();
dev = fmcadc_open(CARD_NAME, dev_id, totalsamples, n_buffers,
FMCADC_F_FLUSH);
if (!dev) {
fprintf(stderr, "%s: fmcadc_open(%s, 0x%x): %s\n", argv[0],
CARD_NAME, dev_id, strerror(errno));
exit(1);
}
if (strcmp(fmcadc_get_driver_type(dev), "zio")) {
fprintf(stderr, "%s: not a zio driver, aborting\n", argv[0]);
exit(1);
}
if (0) { /* We can't change buffer, because chardevs are open */
err = fmcadc_set_param(dev, "cset0/current_buffer", argv[3], 0);
if (err) {
fprintf(stderr, "%s: cannot set '%s' as buffer: %s\n",
argv[0], argv[3], fmcadc_strerror(errno));
exit(1);
}
}
/* FIXME: use nshots to set cset0/chani/buffer/max-buffer-len */
/* FIXME: use maxsize to set cset0/chani/buffer/max-buffer-kb */
/* configure acquisition parameters */
memset(&acq, 0, sizeof(acq));
acq.type = FMCADC_CONF_TYPE_ACQ;
fmcadc_set_conf(&acq, FMCADC_CONF_ACQ_N_SHOTS, nshots);
fmcadc_set_conf(&acq, FMCADC_CONF_ACQ_PRE_SAMP, presamples);
fmcadc_set_conf(&acq, FMCADC_CONF_ACQ_POST_SAMP, postsamples);
err = fmcadc_apply_config(dev, 0, &acq);
if (err) {
fprintf(stderr, "%s: apply_config(acq): %s\n",
argv[0], fmcadc_strerror(errno));
exit(1);
}
/* configure trigger parameters */
memset(&trg, 0, sizeof(trg));
trg.type = FMCADC_CONF_TYPE_TRG;
fmcadc_set_conf(&trg, FMCADC_CONF_TRG_SOURCE, 1); /* external */
err = fmcadc_apply_config(dev, 0, &trg);
if (err) {
fprintf(stderr, "%s: apply_config(trigger): %s\n",
argv[0], fmcadc_strerror(errno));
exit(1);
}
/* Start the acquisition */
err = fmcadc_acq_start(dev, 0 , NULL);
if (err) {
fprintf(stderr, "%s: cannot start acquisition: %s\n",
argv[0], fmcadc_strerror(errno));
exit(1);
}
switch (test) {
case 0:
err = read_with_one_buffer(dev);
break;
case 1:
err = read_with_n_buffer(dev);
break;
}
if (err) {
fprintf(stderr, "%s: problem with read buffer: %s\n",
argv[0], fmcadc_strerror(errno));
exit(1);
}
/* Stop acquisition, close device and exit from library */
fmcadc_acq_stop(dev, 0);
fmcadc_close(dev);
fmcadc_exit();
exit(0);
}
/* Read all shots with a single buffer and then release it */
int read_with_one_buffer(struct fmcadc_dev *dev)
{
struct fmcadc_buffer *buf;
int err = 0, i;
buf = fmcadc_request_buffer(dev, presamples + postsamples, NULL, 0);
for (i = 0; i < nshots; ++i) {
err = fmcadc_fill_buffer(dev, buf, 0, NULL);
if (err)
break;
print_buffer_content(buf);
}
fmcadc_release_buffer(dev, buf, NULL);
return err;
}
/* Read all shots with a multiple buffer and then release them
* request, fill and release are separated in different loop to show that
* are not strictly consecutive operations
*/
int read_with_n_buffer(struct fmcadc_dev *dev)
{
struct fmcadc_buffer *buf[nshots] ;
int err = 0, i;
/* Allocate all buffers before the use */
for (i = 0; i < nshots; ++i)
buf[i] = fmcadc_request_buffer(dev,
presamples + postsamples, NULL, 0);
/* Fill all buffers */
for (i = 0; i < nshots; ++i) {
err = fmcadc_fill_buffer(dev, buf[i], 0, NULL);
if (err)
break;
print_buffer_content(buf[i]);
}
/* Release all buffers */
for (i = 0; i < nshots; ++i)
fmcadc_release_buffer(dev, buf[i], NULL);
return err;
}
void print_buffer_content(struct fmcadc_buffer *buf)
{
int16_t *data = buf->data; /* get data */
struct fmcadc_timestamp *ts;
int i, ch;
ts = fmcadc_tstamp_buffer(buf, NULL);
printf("timestamp %lli:%lli:%lli\n",
(long long)ts->secs,
(long long)ts->ticks,
(long long)ts->bins);
if (!print_data)
return;
for (i = 0; i < presamples + postsamples; i++) {
printf("%5i ", i - presamples);
for (ch = 0; ch < N_CHAN; ch++)
printf("%7i", *(data++));
printf("\n");
}
}
#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