Next Previous Contents

## 6. Writing (BrlAPI-compliant) drivers for brltty

In this chapter, we will describe in details how to write a driver for brltty. We begin with a general description of the structure the driver should have, before explaining more precisely what each function is supposed to do.

## 6.1 Overview of the driver's structure

A braille driver is in fact a library that is either dynamically loaded by brltty at startup, or statically linked to it during the compilation, depending on the options given to the `./configure` script.

This library has to provide every function needed by the core, plus some additional functions, that are not mandatory, but which improve communication with BrlAPI and the service level provided to client applications.

Basically, a driver library needs to provide a function to open the communication with the braille terminal, one to close this communication, one to read key codes from the braille keyboard, and one to write text on the braille display. As we will see in a moment, other functions are required.

Moreover, a driver can provide additional functionalities, by defining some macros asserting that it has these functionalities, and by defining associated functions.

## 6.2 Basic driver structure

Every brltty driver must consist in at least a file called braille.c, located in an appropriate subdirectory of the BrailleDrivers subdirectory. This braille.c file must have the following layout

        #include "prologue.h"
        /* Include standard C headers */
        #include "Programs/brl.h"
        #include "Programs/misc.h"
        #include "Programs/scr.h"
        #include "Programs/message.h"
        /* Include other files */

        static void brl_identify() { }

        static int brl_open(BrailleDisplay *brl, char **parameters, const char *tty) { ... }

        static void brl_close(BrailleDisplay *brl) { ... }

        static void brl_writeWindow(BrailleDisplay *brl) { ... }

        static void brl_writeStatus(BrailleDisplay *brl) { ... }

        static int brl_readCommand(BrailleDisplay *brl, DriverCommandContext context) { ... }

Before giving a detailed description of what each function is supposed to do, we define the `BrailleDisplay` structure, since each function has an argument of type `BrailleDisplay *`. The `BrailleDisplay` structure is defined like this:

        typedef struct {

          int x, y; /* The dimensions of the display */

          int helpPage; /* The page number within the help file */

          unsigned char *buffer; /* The contents of the display */

          unsigned isCoreBuffer:1; /* The core allocated the buffer */

          unsigned resizeRequired:1; /* The display size has changed */

          unsigned int writeDelay;

          void (*bufferResized)(int rows, int columns);

        } BrailleDisplay;

We now describe each function's semantics and calling convention.

The `brl_identify()` function takes no argument and returns nothing. It is called as soon as the driver is loaded, and its purpose is to print some information about the driver in the system log. To achieve this, the only thing this function has to do is to call LOG_PRINT with appropriate arguments (log level and string to put in the syslog).

The `brl_open()` function takes 3 arguments and returns an int. Its purpose is to initialize the communication with the braille terminal. Generally, this function has to open the file referred to by the `tty` argument, and to configure the associated communication port. The `parameters` argument contains parameters passed to the driver with the -B command-line option. It's up to the driver's author to decide wether or not he/she wants to use this argument, and what for. The function can perform some additional tasks such as trying to identify precisely which braille terminal model is connected to the computer, by sending it a request and analyzing its answer. The value that is finally returned depends on the success of the initialization process. If it fails, th function has to return -1. The function returns 0 on success.

The `brl_close()` function takes just one argument, and returns nothing. The name of this function should be self-explanatory; it's goal is to close (finish) the communication between the computer and the braille terminal. In general, the only thing this function has to do is to close the file descriptor associated to the braille terminal's communication port.

The `brl_writeWindow()` function takes just one argument of type BrailleDisplay, and returns nothing. This function displays the specified text on the braille window. This routine is the right place to check if the text that has to be displayed is not already on the braille display, to send it only if necessary. More generally, if the braille terminal supports partial refresh of the display, the calculus of what exactly has to be sent to the braille display to have a proper display, according to what was previously displayed should be done in this function.

The `brl_writeStatus()` function is very similar to `brl_writeWindow()`. The only difference is that whereas `brl_writeWindow()` writes on the main braille display, `brl_writeStatus()` writes on an auxiliary braille display, which occasionaly appears on some braille terminals. The remarks that have been done concerning optimizations for refreshing the display still apply here.

The `brl_readCommand()` function takes two arguments, and returns an integer. Its purpose is to read commands from the braille keyboard and to pass them to brltty's core, which in turn will process them. The first argument, of type `BrailleDisplay`, is for future use, and can safely be ignored for the moment. The second argument indicates in which context (state) brltty is. For instance, it specifies if brltty is in a menu, displays a help screen, etc. This information can indeed be of some interest when translating a key into a command, especially if the keys can have different meanings, depending on the context. So, this function has to read keypresses from the braille keyboard, and to convert them into commands, according to the given context, these commands then being returned to brltty. For a complete list of available command codes, please have a look at `brl.h` in the Programs subdirectory. Two codes have special meanings:

eof

  specifies that no command is available now, and that no key is waiting to be converted into command in a near future.

CMD_NOOP

  specifies that no command is available, but that one will be, soon. As a consequence, brl_readCommand will be called again immediately. Returning CMD_NOOP is appropriate for instance when a key is composed of two consecutive data packets. When the first of them is received, one can expect that the second will arrive quickly, so that trying to read it as soon as possible is a good idea.

## 6.3 Enhancements for BrlAPI

To improve the level of service provided to client applications communicating with braille drivers through BrlAPI, the drivers should declare some additional functions that will then be called by the API when needed.

For each additional feature that has to be implemented in a driver, a specific macro must be defined, in addition to the functions implementing that feature. For the moment, two features are supported by BrlAPI:

- reading braille terminal specific key codes,
- exchanging raw data packets between the braille terminal and a client application running on the PC.

For each feature presented below, only a short description of each concerned macro and function will be given. For a more complete description of concepts used here, please refer to chapters Introduction and General description.

### Exchanging raw data packets

Under some circumstances, an application running on the PC can be interested in a raw level communication with the braille terminal. For instance, to implement a file transfer protocol, commands to display braille or to read keys are not enough. In such a case, one must have a way to send raw data to the terminal, and to receive them from it.

A driver that wants to provide such a mechanism has to define three functions: one to send packets, another one to receive them, and the last one to reset the communication when problems occur.

The macro that declares that a driver is able to transmit packets is:

    #define BRL_HAVE_PACKET_IO

The prototypes of the functions the driver should define are:

    static int brl_writePacket(BrailleDisplay *brl, const unsigned char *packet, int size);
    static int brl_readPacket(BrailleDisplay *brl, unsigned char *p, int size);
    static void brl_rescue(BrailleDisplay *brl)

`brl_writePacket()` sends a packet of `size` bytes, stored at `packet`, to the braille terminal. If the communication protocol allows to determined if a packet has been send properly (e.g. the terminal sends back an acknowledgement for each packet he receives), then this function should wait the acknowledgement, and, if it is not received, retransmission of the packet should take place.

`brl_readPacket()` reads a packet of at most `size` bytes, and stores it at the specified address. The read must not block. I.e., if no packet is available, the function should return immediately, returning 0.

`brl_rescue()` is called by BrlAPI when a client application terminates without properly leaving the raw mode. This function should restore the terminal's state, so that it is able to display text in braille again.

### Remarks.

- If the driver provides such functions, every other functions should use them, instead of trying to communicate directly with the braille terminal. For instance, `readCommand()` should call `readPacket()`, and then extract a key from the packet, rather than reading directly from the communication port's file descriptor. The same applies for `brl_writeWindow()`, which should use `brl_writePacket()`, rather than writing on the communication port's file descriptor.
- For the moment, the argument of type BrailleDisplay can safely be ignored by the functions described here.

Next Previous Contents