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.
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.
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:
specifies that no command is available now, and that no key is waiting to be converted into command in a near future.
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.
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.
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_CODESand 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.
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.
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.
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.