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:

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.

Reading braille key codes

When a client takes control of a tty and asks for getting raw key codes, it has, like in command mode, the possibility to mask some keys. The masked keys will then be passed to brltty. This assumes the existence of a conversion mechanism from key codes to brltty commands. This conversion mechanism can only be implemented by the braille driver, since it is the only piece of code that knows about braille terminal specific key codes. So, to make it possible for client applications to read raw key codes, the driver has to define the following macro:

#define BRL_HAVE_KEY_CODES
and the following functions:
static int brl_readKey(BrailleDisplay *)
int brl_keyToCommand(BrailleDisplay *brl, DriverCommandContext caller, int code)

The semantics of brl_readKey() is very similar to brl_readCommand()'s, with one essential difference: a key code is not context-dependant, so no context argument needs to be given to this function. Moreover, the code this function returns is driver-specific, and has to be properly defined by the driver's author so that client applications can rely on it.

The brl_keyToCommand() function's purpose is to convert a key code as delivered by brl_readKey() into a brltty command. As explained above, this function is called by brlapi when a key is pressed on the braille keyboard that is ignored by the client application. The corresponding command is then returned to brltty.

Remarks

When these two functions are present, the only thing brl_readCommand() has to do is to call brl_readKey() and then call brl_keyToCommand() with the value returned by the first function as argument.

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.


Next Previous Contents