NEXT UP previous
Next: init Function

Header Information

In order to implement the requirements of the specification, the first task is to decide what the device driver's internal data structures will look like. What is required is that the device driver should be capable of storing a number of short text messages which are in transit between two processes. The number of messages the driver is required to hold is supposed to be infinite (infinite on a computer usually means 'until you run out') but in practice it would probably only hold a few messages until they could be read. This means that you really want to use a variable number of dynamically allocated buffers which can be built up into a FIFO queue which is best implemented as a linked list. This will require a kernel mechanism for the dynamic allocation and release of blocks of memory.

Note that the standard library malloc() and free() functions cannot be used because the standard library is not available from within the kernel. As you will see, however, there is a kernel equivalent to malloc() which can be used instead.

The structure from which the linked list of messages will be built has the following layout:

	struct tdd_buf
	{
		int buf_size;
		char buffer[MAX_BUF]; 
		struct tdd_buf *link;
	}

where buffer[] is the array that holds one of the short messages, buf_size says how many characters in the buffer[] array are in use, and link is the linked list pointer to the next tdd_buf. The symbolic constant MAX_BUF can be set to whatever matches your idea of the maximum length of a 'short' message. My default is 120 characters.

Notice that the prefix used in the tiny device driver to make its identifiers unique will be tdd_, though many of the identifiers are declared to be static so that they will not be visible outside the source file for the driver anyway.

Bearing this in mind, the tiny device driver header information is as follows:

	/* 1 */
	*define KERNEL

	#include <linux/kernel.h> 
	#include <linux/sched.h> 
	#include <linux/tty.h>
	#include <linux/signal.h>
	#include <linux/ermo.h>
	#include <linux/malloc.h>

	#include <asm/io.h>
	#include <asm/segment.h> 
	#include <asm/system.h> 
	#include <asm/irq.h>

	#include "tdd.h"

	/* 2 */
	static int tdd_trace;
	static int write_busy; 
	static int read_busy;
	static struct tdd_buf *qhead; 
	static struct tdd_buf *qtail;

	/* 3 */
	static int tdd_read(struct mode *, struct file *, char *, int);
	static int tdd_write(struct mode *, struct file *, char *, mt); 
	static int tdd_ioctl(struct mode *, struct file *, unsigned int, unsigned long);
	static int tdd_open(struct mode *, struct file *);
	static void tdd_release(struct mode *, struct file *); 
	extern void console_print(char *);

	struct file_operations tdd_fops =
	{
		NULL, 
		tdd_read, 
		tdd_write, 
		NULL, 
		NULL, 
		tdd_ioctl, 
		NULL, 
		tdd_open, 
		tdd_release, 
		NULL, 
		NULL, 
		NULL, 
		NULL
	};

The following list refers to the numbered comments in the header information:

  1. The first section includes all the relevant header files. Notice the definition of the KERNEL symbol; this is used in some header files (tdd . h included) conditionally to include extra items and definitions when the header file is being used within kernel code. Ordinary user code does not define this symbol and, therefore, does not include these extras.

  2. These are the static variables used within the driver. The variable tdd_trace is used as a flag to turn on and off the debug trace output, write_busy and read_busy are flags used to prevent multiple writers or readers, and qhead and qtail are the head and tail pointers to the linked list of messages.

  3. This section sets up the file_operations structure to point to the other device driver routines. A pointer to this structure will be passed into the kernel at boot time by the initialization routine. Function prototypes for otherwise undeclared kernel functions are also declared here.

Over and above this header code, there is also a header file called <tdd.h> which contains the #def ines and structure declaration required by the device driver and also by user code wishing to use this driver:

	#ifdef KERNEL                    	  /* If we're in kernel code */

	#define TRACE_TXT(text) \

	if (tdd_trace) \
	{ \
		console_print(text); \
		console_print("\n"); \
	} \
	#define TRACE_CHR(chr) \
	{
		if (tdd_trace) \ 
		console_print(chr); \
	}
	#define TDD_WRITE 0                   /* /dev/tddw minor device number */
	#define TDD_READ 1                    /* /dev/tddr minor device number */

	#endif

	#define FALSE 0
	#define TRUE 1
	#define MAX_BUF 120                    /* Size of struct tdd~buf buffer */
	#define TDD_TRON (('M'<<8)|0x01)       /* Trace on cmd for ioctlO */ 
	#define TDD_TROFF (('M'<<8)|0x02)     /* Trace off cmd for ioctlO */

	struct tdd_buf
	{
	int buf_size; 
	char buffer [MAX_BUF]; 
	struct tdd_buf *link;
	}; 

The only things here which require some explanation are the macro definitions for TRACE_TXT() and TRACE_CHR(). These are provided to give a very simple trace facility for debugging purposes. At various strategic locations throughout the device driver code, calls to these macros are inserted so that 'got here' type messages can be displayed on the machine's console terminal. Trace messages are displayed on the console screen using the kernel's internal function: console_print(). The display of trace messages is controlled by the state of a flag variable whose value can be set or reset via an appropriate ioctl() call. The two commands to ioctl() dealt with by the driver are TDD_TRON to turn tracing on and TDD_TROFF to turn it off again. Neither of these ioctl() commands takes any other parameters.


NEXT UP previous
Next: init Function