From 50af44a63580b60d5204c718c226be1333749c2c Mon Sep 17 00:00:00 2001 From: Alessandro Rubini <rubini@gnudd.com> Date: Wed, 12 Oct 2011 00:10:34 +0200 Subject: [PATCH] Documentation/zio/: new folder These files document how ZIO works. They are still subject to modification while we are writing code, and some information, for example how devices work, are still missing. Signed-off-by: Alessandro Rubini <rubini@gnudd.com> Signed-off-by: Federico Vaga <federico.vaga@gmail.com> --- Documentation/zio/00-INDEX | 10 +++ Documentation/zio/buffer.txt | 106 ++++++++++++++++++++++++ Documentation/zio/device.txt | 68 +++++++++++++++ Documentation/zio/tools/zio-dump.c | 128 +++++++++++++++++++++++++++++ Documentation/zio/trigger.txt | 125 ++++++++++++++++++++++++++++ 5 files changed, 437 insertions(+) create mode 100644 Documentation/zio/00-INDEX create mode 100644 Documentation/zio/buffer.txt create mode 100644 Documentation/zio/device.txt create mode 100644 Documentation/zio/tools/zio-dump.c create mode 100644 Documentation/zio/trigger.txt diff --git a/Documentation/zio/00-INDEX b/Documentation/zio/00-INDEX new file mode 100644 index 0000000..f510695 --- /dev/null +++ b/Documentation/zio/00-INDEX @@ -0,0 +1,10 @@ +00-INDEX + - this file +device.txt + - description of what ZIO devices (and csets and channels) are. +buffer.txt + - description of the buffer, it's methods and how it is used. +trigger.txt + - what is a trigger and what's its role in ZIO. +tools/ + - useful and simple user-space programs \ No newline at end of file diff --git a/Documentation/zio/buffer.txt b/Documentation/zio/buffer.txt new file mode 100644 index 0000000..294fedd --- /dev/null +++ b/Documentation/zio/buffer.txt @@ -0,0 +1,106 @@ + +ZIO defines a "buffer" object type, so each device can exploit any +features it may offer to speed up data transfers. + +Each cset in a device can use a different buffer, which is specified +as an attribute of the cset. One instance of the buffer type exists +for each channel, and the char devices (control and data: see +device.txt) refer to the buffer. + +Please read <linux/zio-buffer.h> together with this file. + + Buffer Types + ============ + +Buffers may be device-specific or generic. A generic buffer called +"kmalloc" is provided within zio-core; it uses kmalloc for allocation +of control and data blocks. Such buffer is activated by default on +all new csets being registered but is not special in any other way, you +can write your own generic buffer (and if it's better than ours, we +may use it as default buffer in future releases). + +A device-specific buffer declares to be such within its attributes. A +device-specific buffer can only be used by csets that declare its name +as preferred buffer type. When such csets are registered, if the +buffer is already known to ZIO, it will be activated by default instead +of "kmalloc". + +Sometimes people write drivers for a set of similar devices, with +similar DMA capabilities. In this case, all of them may refer to the +same device-specific buffer type; the buffer type will be registered +as a standalone kernel module. + +The ZIO buffer data structures includes two sets of operations: +file_operations and buffer_operations. + + + Buffer Operations + ================= + +Each buffer type must provide the following buffer operations. All +of them are implemented in zio-buf-kmalloc.c, which may be used as +reference to better understand the role of each method: + + void *(*create)(struct zio_buffer *buffer, struct zio_channel *ch, + fmode_t f_flags); + void (*destroy)(void *b_instance); + +The create operation allocates and initializes an instance of the +buffer type. It is called by ZIO when a channel is opened for the +first time. The instance is a void pointer because each buffer needs a +different (private) data structure to represent an own instance. + +Destroy deallocates the buffer. ZIO calls destroy when the channel +is unregistered from ZIO or when the user assigns a different buffer +type to the channel. + +When the control/data devices are closed, ZIO doesn't call destroy, so +incoming data can queue up while the application leaves it closed +(e.g., a shell script). Actually, the release file operation is not +handled by ZIO, so it couldn't call destroy on close even if it wanted +to. + + void *(*alloc_block)(void *b_instance, void **handle_ret, + size_t size, gfp_t gfp); + void (*free_block)(void *handle); + +For input channels, a block is allocated when the trigger fills it, +and it is freed when user has read or ignored it. For output, +allocation happens on user write and free is called by the trigger. +Thus, the function are sometimes called by buffer code itself, and +sometimes by the trigger. The data structure hosting a block is +returned as a void pointer because each buffer needs its own +private structure. + + ssize_t (*store_block)(void *handle, struct zio_control *ctrl, + void *data, size_t len); + ssize_t (*retr_block) (void *handle, struct zio_control *ctrl, + void *data, size_t len); + +The trigger calls the store method when it has data to push for input +channels. If uses an handle it received from the alloc_block method. +The trigger for output channels uses the retrieve method to extract +data from the buffer and send it to the hardware driver. After +retrieving data, it is responsible for freeing it. +FIXME: how can the trigger know the handle to retrieve? + + + File Operations + =============== + +These are the file_operations used by the char devices (control and +data) for every channel using this buffer type. The open method of +the zio file operations kmallocs f->private_data to point to this +structure: + + struct zio_f_priv { + void *b_instance; + enum zio_cdev_type type; + }; + +All buffer file operations can thus refer to the buffer instance for +this channel and know if the current file is ZIO_CDEV_CTRL or +ZIO_CDEV_DATA. The release file operation is expected to free this +structure. + +See zio-buf-kmalloc.c for a working example of buffer file operations. diff --git a/Documentation/zio/device.txt b/Documentation/zio/device.txt new file mode 100644 index 0000000..75bd102 --- /dev/null +++ b/Documentation/zio/device.txt @@ -0,0 +1,68 @@ + + ZIO Device + ========== + +A device, registered through zio_register_device() is the description +of an I/O peripheral. It is made up of channel-sets, called csets +from now on. A device may represent a PCI board or an SPI integrated +circuit or whatever it makes sense to manage from a single device driver. +All I/O operations are performed on csets, so the device is just an +array of csets. + + Csets + ===== + +A cset (channel-set) is an homogeneous set of I/O channels. All +channels in the set are feature the same physical characteristics; +moreover, a cset refers to a trigger object, so all channels in a set +are triggered by the same event. This is the typical use case for +logic analysers (digital input) or multi-probe scopes (analog input), +as well as multi-waveform output. If your device has several input +channels which are separately triggered, they should be defined as +several cset items, each featuring one channel only. Finally, all +channels in a cset use the same buffer object for in-kernel data +storage. Both the buffer and the trigger for a cset are set by +writing the proper name in sysfs. At device registration defaults +apply. If ZIO can't find the trigger/buffer name you wrote, +it will return EINVAL and leave the previous trigger/buffer in place. + + Channels + ======== + +The channel is the lowest object in the ZIO hierarchy. It represents +the single physical connector of the device: analog or digital, input +or output. Time-to-digital and digital-to-time devices can be represented +as channels as well. + + Attributes + ========== + +Most configuration and information in ZIO happens through sysfs. +See sysfs.txt for information about attributes. (FIXME: sysfs.txt) + + Data Transfers + ============== + +Data transfer in ZIO uses two char devices for each channel offered by +the driver. One char device is used to describe data blocks (we call +it control device); the other is used to transfer the data blocks (we +call it data device). The control device returns (or accepts) a +fixed-size structure that describes the next data transfer, including +the trigger in use, data size, number of samples and various +attributes. Applications may choose to read the data device alone, +without retrieving control information: when any data of a new block +is transferred, the associated control information is discarded. +Similarly, data is discarded if you re-read the control device after +having retrieved the description of a data block you are not +interested in. For output, writing data without writing control uses +the default control information, or the one from the previous +transfer). + +The full set of rules for data and control transfers is described +elsewhere (FIXME: link to other docs) but it is pretty intuitive once +you get the idea. + +See tools/zio-dump.c for an example of generic and simple input +application that shows use of control and data files. + + diff --git a/Documentation/zio/tools/zio-dump.c b/Documentation/zio/tools/zio-dump.c new file mode 100644 index 0000000..b9b81e8 --- /dev/null +++ b/Documentation/zio/tools/zio-dump.c @@ -0,0 +1,128 @@ +/* + * Trivial utility that reports data from ZIO input channels + */ +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "zio.h" +#include "zio-buffer.h" + +#define FD_C 0 +#define FD_D 1 + +unsigned char buf[1024*1024]; + +int main(int argc, char **argv) +{ + FILE *f; + char *outfname; + int fd[2]; + int i, j; + + if (argc !=3) { + fprintf(stderr, "%s: use \"%s <ctrl-file> <data-file>\"\n", + argv[0], argv[0]); + exit(1); + } + + for (i = 0; i < 2; i++) { + fd[i] = open(argv[i + 1], O_RDONLY); + if (fd[i] < 0) { + fprintf(stderr, "%s: %s: %s\n", argv[0], argv[i + 1], + strerror(errno)); + exit(1); + } + } + /* the data channel is non-blocking */ + fcntl(fd[FD_D], F_SETFL, fcntl(fd[FD_D], F_GETFL) | O_NONBLOCK); + + /* always log data we read to some filename */ + outfname = getenv("ZIO_DUMP_TO"); + if (!outfname) + outfname = "/dev/null"; + f = fopen(outfname, "w"); + if (!f) { + fprintf(stderr, "%s: %s: %s\n", argv[0], outfname, + strerror(errno)); + exit(1); + } + /* ensure proper strace information and output to pipes */ + setlinebuf(stdout); + setbuf(f, NULL); + + /* now read control and data, forever */ + while (1) { + zio_control_t ctrl; + + /* This is a blocking read to the control file */ + i = read(fd[FD_C], &ctrl, sizeof(ctrl)); + switch(i) { + case -1: + fprintf(stderr, "%s: %s: read(): %s\n", + argv[0], argv[1 + FD_C], strerror(errno)); + exit(1); + case 0: + fprintf(stderr, "%s: %s: unexpected EOF\n", + argv[0], argv[1 + FD_C], strerror(errno)); + exit(1); + default: + fprintf(stderr, "%s: ctrl: read %i bytes (exp %i)\n", + argv[0], i, sizeof(ctrl)); + /* continue anyways */ + case sizeof(ctrl): + break; /* ok */ + } + printf("Ctrl: n %i, size %i, bits %i, flags %08x\n", + ctrl.ctrl.nsamples, + ctrl.ctrl.ssize, + ctrl.ctrl.sbits, + ctrl.ctrl.flags); + printf("Ctrl: stamp %li.%09li (%lli)\n", + ctrl.ctrl.tstamp.tv_sec, + ctrl.ctrl.tstamp.tv_nsec, + ctrl.ctrl.tstamp_extra); + /* FIXME: some control information is missing */ + + i = read(fd[FD_D], buf, sizeof(buf)); + if (i < 0) { + fprintf(stderr, "%s: %s: read(): %s\n", + argv[0], argv[1 + FD_D], strerror(errno)); + continue; /* next ctrl, let's see... */ + } + if (!i) { + fprintf(stderr, "%s: %s: unexpected EOF\n", + argv[0], argv[1 + FD_D], strerror(errno)); + continue; + } + if (i != ctrl.ctrl.nsamples * ctrl.ctrl.ssize) { + if (i == sizeof(buf)) { + fprintf(stderr, "%s: buffer too small\n", + argv[0]); + /* FIXME: empty the data channel */ + } else { + fprintf(stderr, "%s: ctrl: read %i bytes " + "(exp %i)\n", argv[0], i, + ctrl.ctrl.nsamples * ctrl.ctrl.ssize); + } + /* continue anyways */ + } + fwrite(buf, 1, i, f); + + /* report data to stdout */ + for (j = 0; j < i; j++) { + if (!(j & 0xf)) + printf("Data:"); + printf(" %02x", buf[j]); + if ((j & 0xf) == 0xf || j == i - 1) + putchar('\n'); + } + putchar('\n'); + } +} diff --git a/Documentation/zio/trigger.txt b/Documentation/zio/trigger.txt new file mode 100644 index 0000000..491d59b --- /dev/null +++ b/Documentation/zio/trigger.txt @@ -0,0 +1,125 @@ + +ZIO defines a "trigger" object type, and each cset is connected to +a trigger. + +Each cset in a device can use a different trigger, which is specified +as an attribute of the cset. When the trigger fires, it acts on all +the non-disabled channels of the cset. Only the "app-request" trigger +can act on a single channel at a time. + + + Trigger Types + ============= + +Triggers may be device-specific or generic. A few generic triggers +are part of zio-core. The "app-request" trigger fires input when +the application calls read and fires output when the application calls +write (it acts on a single channel). The "ktimer" trigger uses a kernel +timer as trigger source. The "irq" trigger uses any interrupt (e.g., +a GPIO interrupt, or pin 10 of the PC parallel port) as trigger event. + +A device-specific trigger declares to be such within its attributes. A +device-specific trigger can only be used by csets that declare its name +as preferred trigger type. When such csets are registered, if the +trigger is already known to ZIO, it will be activated by default instead +of "app-request". + + Trigger Operations + ================== + +(NOTE: this is still subject to fine-tuning, as we are still writing code) + +Trigger operations are the following: + + struct zio_trigger_instance *(*create)(struct zio_buffer *buffer, + struct zio_cset *cset); + void (*destroy)(struct zio_trigger_instance *t_instance); + +Create and destroy a trigger instance for a cset. ZIO calls create when +attaching the trigger to a cset; it calls destroy when the trigger is +replaced by a different one or the cset is being unregistered from ZIO. +The instance structure is trigger-specific, but it must include this +generic structure: + + struct zio_trigger_instance { + struct zio_trigger *trig; + struct zio_cset *cset; + }; + +Every time this structure is passed over, trigger code may use container_of +if it needs to access the private enclosing structure. + + int (*config)(struct zio_trigger_instance *t_instance); + +The method is called by ZIO whenever the attributes for a trigger +instance are modified by the user (by writing to sysfs or otherwise). + + int (*enqueue_block)(struct zio_trigger_instance *t_instance, + struct zio_channel *ch, + struct zio_control *ctrl, + void *data, size_t len); + +This is used for output channels: when a new data block is ready, it +must be sent to the trigger so it can be output when the event fires. +Buffer code, therefore, is expected to call this trigger method. The +function can return -EAGAIN if it has no space in the queue, or 0 on +success. If EAGAIN happens, the buffer should handle it (by storing +locally or notifying the user). + + int (*space_in_buffer)(struct zio_trigger_instance *t_instance); + +The method return either 0 (there is space for one block), a positive +value (the maximum size of the next block that can be enqueued) or +-EAGAIN. This can be used by the poll file operation, to ask +whether the next write(2) will succeed or not. + + File Operations + =============== + +The trigger may include a non-NULL f_ops pointer. Most trigger will +not need it, but for example "app-request" does, because it needs to +look at individual read and write calls performed by applications. +ZIO will use these file operations (instead of the buffer file operations) +when the open method of the char device detects that the active trigger +declares a non-NULL f_ops field. These operations will most +likely fall back to buffer->f_ops for most of their actual work. + +See zio-trig-app-request.c for details about how this is used. + + When the trigger fires + ====================== + +The trigger event may happen for a variety of reasons. It can be +time-driven, data-driven or whatever else. In any case, there is +a time when the trigger fires, so input or output may happen. +(With most hardware-specific triggers, the actual input or output of +data has already happened when the trigger interrupt runs, but this +doesn't change the software flow). + +For output triggers, the trigger instance is already hosting the data +blocks (received through the enqueue_block method), so the code will +just loop over all the channels and free such data blocks. In some cases +the trigger will need to perform the output before freeing data, in +most cases data has been prepared for DMA during enqueue_block, +so output already happened when the trigger run. + +ZIO offers this help macro to loop over all non-disabled channels: + + cset_for_each(struct zio_cset *cset, struct zio_channel *ch) + +The macro works like "task_for_each" or "list_for_each" in the kernel +headers. + +For input triggers, the asynchronous code that runs the event will just +need to call + + zio_fire_trigger(struct zio_trigger_instance *instance); + +This function, part of zio-core, internally runs "cset_for_each". +For each non-disabled channel, it calls the drv->input_block method +and the stores it in the active buffer. + +You can refer to "zio-trig-ktimer" for an example of a multi-instance +generic timer and to "zio-trig-app-request" for a non-conventional +implementation based on trigger-local file_operations. + -- GitLab