The Driver Description Language (DDL) is an interface definition language, that describes interface handling methods to the kernel or from kernel (driver) to the user process. It consists of a mixup between pure DDL statements, macro expansion expressions and inlined C-code. Inlined C-code statemens or templates usualy contains macros that are expansed when the code is generated, some of this macros are defined implicitely so as the module name or the lddk version.
Each DDL module consists of a module header where the module name is declared and codeclass statements. Each codeclass describes an interface to the kernel or an interface to the user (for example the driver codeclass describes the interface for a classical unix driver, but it would be also imaginable to have codeclasses for filesystems or scsi interfaces, network protocolls or whatever). Depending on the implementation of a codeclass it can have options that controls certain global features of the generated interface. (one option of the driver codeclass is the major number for example)
Inside a codeclass the methods for handling certain tasks in the codeclass are described. A method can consist of a simple name and an inlined code statements but sometimes of a set of submethods. Additionally some codeclasses define special methods for administrative tasks such as generating header files ,c-code files that are bundled with the driver module later or just defining or overriding a macro. This provides maximal flexibility if the user needs to write own code for special tasks where no codeclass is defined.
There are some special statements that have a special meaning in DDL:
/{ c-code }/
encloses a inlined C-code section
%< macro>
encloses a macro processing tag (only inside a C-code statement or template)
"a string"
encloses a string
/* comment */
encloses a DDL comment (this will not appear in the C files later)
/@ description @/
define a description tag.
[ options ]
used for options (optional, can be ommited)
include filename
includes a ddl file.
Each DDL module begins with a module header statement:
module "name" :
this statements sets the name of the driver module and thus the name of the sourcetree directory.
driver "name" [options] { methods }
The driver codeclass is the basic codeclass that defines all methods for a classical unix driver module. Additionally there are some special functionalities specific to linux.
With the major=number or minor[=number] options main-module or submodule drivers can be generated. Normally the driver class that has a major defined is the main module. Submodules can hook up methods under a certain minor number, in this case the minor option is used with a number. all requests to this minor number will call the routines in this class. In this case the main module only has to implement the core methods and add a open-submodules method that causes the generation of the appropriate open() routine. If no number is given to the minor option a number will be generated automatically.
core { submethods }
The core method defines the basic routines necessary for loading and unloading a dynamic loadable kernel module (You will need this if your driver is intended to work). It defines two submethods:
init /{ C-code }/
is called when the module is loaded into the kernel with insmod. It registers all the other methods of driver into the kernel implicitely.
cleanup /{ C-code }/
is called when the module is unloaded with rmmod.It unregisters your driver from kernel.
open /{ C-code }/
Implicit generated Parameters:
struct inode* inode, struct file* file
Implements the open() call to a driver.
close /{ C-code }/
struct inode* inode, struct file* file
Implements the close() call to adriver.
read /{ C-code }/
struct inode* inode, struct file* file, char * buffer, int count
write /{ C-code }/
struct inode* inode, struct file* file, char * buffer, int count
mmap /{ C-code }/
struct inode* inode, struct file* file, struct vm_area_struct * vma
lseek /{ C- code }/
not implemented yet
select /{ C-code }/
not implemented yet
ioctl { submethods }
Implicit parameters:
struct inode* inode, struct file* file, unsigned int cmd, unsigned long arg
The ioctl method is used to define ioctl routines. Normally driver writers have to take care about IOCTL numbers and parameter handling inside the kernel. With the ioctl func statement in LDDK implementing ioctls is as easy as writing a normal C-Program since all this tasks are handled in background. The user may define a function with the func submethod of ioctl and the driver generator will build two corresponding routines one in the kernel driver (called the kernel stub) and one in the user library (called the library stub). The calling conventions for both routines are the same except the additonal file-descriptor parameter in the librarys stub routine. Everything that is needed to build the routine with the driver, generating IOCTL numbers or handling header files will be done automatically.
func functionname ( parameters ) /{ code }/
Defines two corresponding routines, the driver stub
int Drivername_functionname ( parameters ) { code }
and the corresponding library stub routine:
int Drivername_functionname ( int fd, parameters )
The first-level unpacking of the parameters will be done by the resulting code, so the user will only need to call the memcpy_fromfs() if he wants to read from a pointer. In the library stub the user just has to open the driver and use the file descriptor to the corresponding stub routine.
If the user wants to use old ioctl code it can be added to the ioctl dispatch routine using the
user /{ code }/
submethod. In the code section of this method, the user can specify additional
case IOCTL_NR:
my code....
break;
statements to the dispatcher.
sysctl { submethods }
The sysctl interface defines some kind of 'global variables' that are acessible through the sysctl interface (virtual files in /proc/sys). Whenever a driver codeclass uses this statement the driver will have its own subdirectory in /proc/sys where the variable names are stored. The user writes strings to the sysctl interface that the driver has to parse and store the result in the appropriate variables. LDDK has default handlers to do this but users may specify own handler routines to accept own setting styles. Currently the following variable types are implemented.
int name [ options ] = default
Defines an integer, default is the initial value of this variable stored at driver loading time. it can be overrided by insmod name=value.
string name [options] = "default"
Defines a string variable . The length of the initial value controls the maximal length of the resulting variable.
The options statement controls different properties of the handling or the resulting inode in /proc/sys, for example a variable can be set to read-only using the access method:
int base [ access=0444 ] = 0x200
This will create a variable "base" in the driver module and a corresponding inode in /proc/sys/<drivername>/ that can be viewed using cat. the variable cannot be set because the access mode is r--r--r-- .
With the handler=handlername an user defined handler routine can be defined that have been defined with the 'handler' submethod (see the sysctl.dll example how to use it). The handler normally does the parsing of the user string or does the output formatting if the variable is read out.
procinfo /{ code }/
This method defines a procinfo routine inside the driver that can be used to display status information (buffer sizes, error counts or whatever) in /proc/<drivername>. See the Procinfo.ddl example for further details.
The library codeclass is intended to implement self contained user library routines, provided with a LDDK driver. Library functions are using the documentation statements in similar way as the ioctl functions. Additionally an user library function is exported to the scripting language file.
func [ returns type ] name ( parameters ) /{ code }/
Will generate a file named name in the library subtree which defines the routine and a corresponding entry in the language .i file to specify the corresponding interface to the scripting language.
Each codeclass has some common methods defined to add user defined files in the sourcetree. For this purpose there are two methods defined in each codeclass:
header