Tuesday, September 27, 2011

Custom device driver in an RTOS (With Buffered I/O)


Driver functionalities

1. Driver initialization    
    Initialize the actual hardware device
    Initialize the internal data structres

2. Driver control
    Run-time control of hardware device
    e.g.: Change the baud rate of a serial device

3. Driver access
    In applications where multiple threads need simultaneous driver acces - use semaphore

4. Driver input/output
    How input/output is buffered and how threads wait on them

5. Driver interrupts
    Interrupts notify the driver of device input,output, control & error events.

6. Driver status
    Run time status & statistics associated with the drive operation.

7. Driver termination
    If the driver and/or the physical hardware device need to be shut down.


 
Example: Simple serial hardware device with a configuration register, and input register and an output register
Initialization
Two counting semaphores are used to manage the driver's input and output operation.
Input semaphore: Set by input ISR when a character is received by the serial hardware device
         Semaphore is created with an initial value of 0.
Output semaphore: Indicates availability of the serial hardware transmit register
          Semaphore is created with an initial value of 1 to indicate availability.

void sdriver_initialize(void)
{
    /* Initialize the two counting semaphores user to control the simple I/O */
    tx_semaphore_create(&sdriver_input_semaphore,"Sdriver Input Semaphore",0);
    tx_semaphore_create(&sdriver_output_semaphore,"Sdriver Output Semaphore",1);

    /* Setup interrupt vectors for input and output ISRs.
       The initial vector handling should call the ISRs
       defined in this file.*/

    /* Configure serial device hardware for RX/TX interrupt
       generation, baud reate, stop bits, etc. */
}

Simple driver input:
When a serial device interrupt is received, the input semaphore is set.
If one or more threads are waiting for a character from the driver, the
thread waiting the longest is resumed.
Limitations: Dropping input characters
Handled by: Add input character buffer

unsigned char sdriver_input(void)
{
    /*determine if there is a character waiting. If not, suspend */
    tx_semaphore_get(&sdriver_input_semaphore, TX_WAIT_FOREVER);
  
    /* Return character from serial RX hardware register */
    return(*serial_hardware_input_ptr);
}

void sdriver_input_isr(void)
{
    /* See if an input character notification is pending */
    if(!sdriver_input_semaphore.tx_semaphore_count)
    {
        /* If not, notify thread of an input character */
        tx_semaphore_put(&sdriver_input_semaphore);
    }
}

Simple driver output:
Output semaphore is used to signal when the serial device's transmit register is free.

void sdriver_output(unsigned char alpha)
{
    /* Determine if the hardware is ready to transmit a character.
       If not, suspend until the previous output completes. */
    tx_semaphore_get(&sdriver_output_semaphore, TX_WAIT_FOREVER);

    /* Send the character through the hardware */
    *serial_hardware_output_ptr = alpha;
}

void sdriver_output_isr(void)
{
    /* Notify thread last character transmit is complete */
    tx_semaphore_put(&sdriver_output_semaphore);
}

Shortcomings: Illustrates the basic idea of a simple driver.
          Doesn't address data buff
ering or any overhead issues.


 
Advanced driver issues:

1. Logic for Circular input buffer

unsigned char input_buffer[MAX_SIZE];
unsigned char *input_write_ptr;
unsigned char *input_read_ptr;

/* Initialization */
input_write_ptr =  &input_buffer[0];
input_read_ptr  =  &input_buffer[0];

/* Input byte ISR... alpha has character from device */
save_ptr = input_write_ptr;
*input_write_ptr++ = alpha;
if(input_write_ptr > &input_buffer[MAX_SIZE-1])
    input_write_ptr = &input_buffer[0]; /* Wrap */
if(input_write_ptr == input_read_ptr)
    input_write_ptr = save_ptr; /* Buffer full */

/* Retrieve input byte from the buffer */
if(input_read_ptr != input_write_ptr)
{
    alpha = *input_read_ptr++;
    if(input_read_ptr > &input_buffer[MAX_SIZE -1 ])
        input_read_ptr = &input_buffer[0];
}

2. Logic for Circular output buffer

unsigned char output_buffer[MAX_SIZE];
unsigned char *output_write_ptr;
unsinged char *output_read_ptr;

/* Initialization */
output_write_ptr = &output_buffer[0];
output_read_ptr  = &output_buffer[0];

/* Transmit complete ISR... Device ready to send */
if(output_read_ptr != output_write_ptr)
{
    *device_reg = *output_read_ptr++;
    if(output_read_ptr > &output_buffer[MAX_SIZE-1])
        output_read_ptr = &output_buffer[0];
}


/* Output drive buffer service. If device busy, buffer! */
save_ptr = output_write_ptr;
*output_write_ptr++ =  alpha;
if(output_write_ptr > &output_buffer[MAX_SIZE -1 ] )
    output_write_ptr = &output_buffer[0]; /* Wrap */
if(outpu_write_ptr == output_read_ptr)
    output_write_ptr = save_ptr; /* Buffer full */

No comments: