Streams

Streams are the abstractions for exchanging data with anything: a local or distant file, a device through serial or USB communications, local memory (usually for simulation), etc.

The stream object is usable both by the library and the user. The library defines some useful platform-agnostic streams it uses itself, such as the checksum stream (which calculates the checksum as the file content is read), and some platform-specific streams for platforms defining a macro.

tio_stream_t

An object representing a stream.

A stream as defined in libtio is a handle representing an I/O device or file, which can have one of these types:

TIO_TYPE_GENERIC

The stream is a basic I/O stream (as FILE) on which you can read, write and sometimes seek. It can be used for files.

TIO_TYPE_SERIAL

The stream is an I/O stream on a serial line on which you can read, write and set the attributes (settings) and timeouts.

TIO_TYPE_USB

The stream is a USB device which you can get and USB packets on.

TIO_TYPE_SCSI

The stream is an SCSI device to which you can send SCSI requests to.

Streams can be defined on top of each other with the concept of “parent streams”: for example, a USB storage key will use SCSI over USB, by the use of an SCSI stream defined onto a USB stream. This allows you to send and receive USB packets as well as SCSI requests on the SCSI stream, which can be useful if the device supports some custom USB interactions.

This document describes how to use and make a libtio stream. Notice that for the examples in this document, you shall include <libtio.h>, or the stream-specific header, <libtio/stream.h>.

Opening and closing a stream

There are two ways to open a stream:

  • with a specialized function, such as tio_open_unix_fd() for POSIX;

  • with a platform-agnostic function, such as tio_open_usb();

  • with the core function, tio_open(), which you will use when you will want to define your own streams.

All of the stream opening functions shall take a reference to the stream pointer you’ll use as the stream handle as the first parameter, and return the libtio error code that occurred.

Once you are done with the stream, you shall close it, so that all of the allocated resources can be free’d properly. In order to do this, you can simply use tio_close().

int tio_open_any_stream(tio_stream_t **stream, ...);
int tio_close(tio_stream_t *stream);

So here’s a simple example using the tio_open_memory() function (which makes a stream out of memory), for opening a stream and directly closing it:

#include <libtio.h>

void do_something()
{
        int err;
        tio_stream_t *stream = NULL;
        char zone[6];

        err = tio_open_memory(&stream, zone, 6);
        if (err) {
                /* an error has occured! */
                return ;
        }

        /* do something here, if an error occurs, make sure to
           close the stream anyway, or use the 'fail' label here */

fail:
        err = tio_close(stream);
        /* you can check the error if you want, but the stream will always
           be closed at this point, more or less properly */
}

Getting the stream properties

You can use the following functions to get properties from any stream:

int tio_get_type(tio_stream_t *stream)

Returns the stream type.

Returns the stream cookie (hoping that you have the structure to it, because the library probably doesn’t).

int tio_get_lasterr(tio_stream_t *stream)

Returns the last error that has occurred with the stream. Prefer getting the error directly for simple operation failures.

int tio_get_parent(tio_stream_t *stream, tio_stream_t **parentp)

Get the parent stream pointer.

And in order to check if you can issue some operations to a stream, you can use the following functions:

int tio_is_readable(tio_stream_t *stream)

Check if the stream is readable.

int tio_is_writable(tio_stream_t *stream)

Check if the stream is writable.

int tio_is_seekable(tio_stream_t *stream)

Check if the stream is seekable.

Reading and writing to a stream

These functions both work for generic and serial streams.

int tio_set_timeouts(tio_stream_t *stream, tio_timeouts_t const *read_timeouts, tio_timeouts_t const *write_timeouts)

Set the stream timeouts for read and write operations.

int tio_read(tio_stream_t *stream, void *dest, size_t count)

Read exactly count bytes from the stream into dest, while complying with the stream timeouts.

int tio_read_tm(tio_stream_t *stream, void *dest, size_t count, tio_timeouts_t const *tm)

Read exactly count bytes from the stream into dest, while complying with the timeouts defined in tm.

int tio_write(tio_stream_t *stream, void const *data, size_t count)

Write exactly count bytes to the stream from data, while complying with the stream timeouts.

int tio_write_tm(tio_stream_t *stream, void *dest, size_t count, tio_timeouts_t const *tm)

Write exactly count bytes to the stream from data, while complying with the timeouts defined in tm.

int tio_write_char(tio_stream_t *stream, int char)

Write a byte to the stream.

int tio_skip(tio_stream_t *stream, tio_off_t size)

Skip exactly size bytes on the stream. For seekable generic streams, it is actually an equivalent to the following call:

tio_seek(stream, TIO_SEEK_CUR, size);

Using a generic stream

The following functions ought to be used with generic streams:

int tio_seek(tio_stream_t *stream, tio_off_t offset, tio_whence_t whence)

Seek to a position in the stream, depending on whence:

TIO_SEEK_SET

Seek to the stream start plus offset.

TIO_SEEK_CUR

Seek to the stream current cursor position plus offset.

TIO_SEEK_END

Seek to the stream end minus offset.

int tio_get_size(tio_stream_t *stream, tio_off_t *size)

Find out the stream size (using tio_seek() behind as a fallback).

Using a serial stream

Serial streams have attributes (or settings), represented using the following type:

tio_serial_attrs

The link settings of a serial stream.

unsigned int tio_serial_attrs_flags

The various link settings flags (todo).

unsigned long tio_serial_speed

The speed in bauds of the link.

unsigned char tio_serial_attrs_cc

The special characters for the link.

The following functions ought to be used with serial streams:

int tio_make_serial_attrs(tio_serial_attrs_t *attrs, char const *raw)

Initialize serial attributes, possibly from a string which can be used for command-line interfaces (e.g. 19200N2).

int tio_get_serial_attrs(tio_stream_t *stream, unsigned int flags, tio_serial_attrs_t *attrs)

Get the current serial attributes from a stream.

int tio_set_serial_attrs(tio_stream_t *stream, unsigned int flags, tio_serial_attrs_t const *attrs)

Set the current serial attributes on the stream.

Using a USB stream

The following functions ought to be used with USB streams:

int tio_usb_send_bulk(tio_stream_t *stream, unsigned char const *data, size_t size, unsigned int timeout)

Send data using USB bulk packets.

int tio_usb_recv_bulk(tio_stream_t *stream, unsigned char *buf, size_t size, unsigned int timeout)

Receive data from the next USB bulk packet.

Using an SCSI stream

The following functions ought to be used with serial streams:

int tio_scsi_request(tio_stream_t *stream, tio_scsi_request_t *request)

Send a SCSI request and get answers.

Which mainly uses the following structure:

tio_scsi_request_t

The request input and output structure.

void *tio_scsi_request_cmd

The command (as a 8, 12 or 16 bytes data block).

size_t tio_scsi_request_cmd_len

The length of the command data pointed above.

int tio_scsi_request_direction

The data direction, as one of the TIO_SCSI_DIREC_* constants.

void *tio_scsi_request_data

The data to transfer or a pointer to the buffer to fill, depending on the direction given above.

size_t tio_scsi_request_data_len

The length of the data or buffer pointed above.

int tio_scsi_request_status

The status byte returned by the device.