Hardware Interaction Within the Minidriver

The following example shows how to interact with hardware from within a minidriver. It's for a fictional hardware device called “MYBUS” with the following characteristics:

Note: Remember that the mapping of hardware registers depends on where in the boot process that the minidriver is called. This transition is handled in the MDRIVER_STARTUP_PREPARE and MDRIVER_STARTUP_FINI stages.
#include "startup.h"      /* This is included with the BSP for your board */

   typedef unsigned char  U8;
   typedef unsigned short U16;
   typedef unsigned int   U32;

/**************    MYBUS Registers   ********************/

typedef struct MYBUS_register_set {
   volatile U8  interrupt_status;
   volatile U8  data_register;
   volatile U8  control_register;
   volatile U8  extra1;
   volatile U8  extra2;
   volatile U8  extra3;
   volatile U8  extra4;
   volatile U8  extra5;
}  MYBUS_regs_t;

/**************    GPIO Registers   ********************/

typedef struct GPIO_register_set {
   volatile U32  gpio0;
   volatile U32  gpio1;
}  GPIO_regs_t;

/**************    Minidriver data area   ************/

typedef struct
{
   MYBUS_regs    *MYBUS_REGS;                   /* This is the same as either
                                                   PREKERNEL or POSTKERNEL. */
   MYBUS_regs    *MYBUS_REGS_PREKERNEL_START;   /* Register mappings to 
                                                   use before the kernel starts. */
   MYBUS_regs    *MYBUS_REGS_POSTKERNEL_START;  /* Register mappings to use 
                                                   after the kernel starts. */
   U16           total_message_counter;         /* Total times the minihandler 
                                                   is called. */
   U16           process_counter;               /* Times called after the kernel 
                                                   is running. */
   U16           kernel_counter;                /* Times called while the
                                                   kernel is booting. */
   U16           data_len;                      /* Length of data portion 
                                                   stored in the data area. */
}MYBUS_data_t;


/* Physical memory locations and offsets */

#define MBAR_BASE 0xff000000
#define GPIO_OFFSET 0x0C00
#define MYBUS_OFFSET 0x2400

/* Control_register settings */
#define CTRL_INTERRUPT_ON       0x01
#define CTRL_INTERRUPT_OFF      0x00

/*********************************************************
void MYBUS_Init(void)

Hardware initialization function for MYBUS.
This routine is called only once, when the minidriver is started.

INPUTS   None

OUTPUTS  None

 *********************************************************/
 
static MYBUS_regs_t * MYBUS_Init(void)
{
   GPIO_regs_t   *GPIO_REGS_P;
   MYBUS_regs_t    *MYBUS_REGS_P;
   U32 data_byte;

   if((GPIO_REGS_P =
        (GPIO_regs_t *) startup_memory_map(0x40, MBAR_BASE + GPIO_OFFSET), 
        PROT_READ|PROT_WRITE|PROT_NOCACHE)) == 0 )
      {
          startup_memory_unmap((unsigned)GPIO_REGS_P);
          return (0);
      }

      /* Change GPIO as needed */
      Data = GPIO_REGS_P->gpio0;
      Data = Data & 0xFFF0FFFF;
      GPIO_REGS_P->gpio0 = Data;

      /* We are done with GPIO */
      startup_memory_unmap((void *)PORT_REGS_P);

      if((MYBUS_REGS_P 
         = (MYBUS_regs_t *)startup_memory_map(0x10, 
           (MBAR_BASE + MYBUS_OFFSET), 
           PROT_READ|PROT_WRITE|PROT_NOCACHE)) == 0
         {
             startup_memory_unmap((unsigned)MYBUS_REGS_P);
             return (0);
         }

        /* Initialize MYBUS and turn on the interrupt. */

        /* Write any values to the MYBUS_REGS_P as needed, and
           then turn on the interrupt source. */

        MYBUS_REGS_P->control_register = CTRL_INTERRUPT_ON;

        kprintf("MYBUS is initialized\n" );
        return ( MYBUS_REGS_P );

}

/*********************************************************
   int mini_mybus_handler(void)
 *********************************************************/

int mini_mybus_handler(int state, void *data)
{
   U8              *dptr;
   U8 StatusReg;
   U8 notValid;
   MYBUS_data_t    *mdata;
   int             val;

   mdata = (MYBUS_data_t *) data;
   dptr = data + sizeof(MYBUS_data_t);

   if (state == MDRIVER_INTR_ATTACH)
   {
       kprintf("Real driver is attaching .. minidriver was called %d times\n",
               mdata->total_message_counter);

       /* Disable MYBUS interrupt */
       mdata->MYBUS_REGS_POSTKERNEL->control_register = CTRL_INTERRUPT_OFF;
       return (1);
   }
   else if (state == MDRIVER_INIT)
   {
      /* The first time called, initialize the hardware and do data setup */
      mdata->MYBUS_REGS_PREKERNEL = MYBUS_Init();
      if (mdata->MYBUS_REGS_PREKERNEL == 0)
      {
         return (1);
      }

      /* Make our default register location reflect the fact that we are
         in PREKERNEL */
      mdata->MYBUS_REGS = mdata->MYBUS_REGS_PREKERNEL;

      /* Initialize the counters of messages received. */
      mdata->total_message_counter = 0;
      mdata->process_counter = 0;
      mdata->kernel_counter = 0;
  }
  else if (state == MDRIVER_PROCESS)
  {
      mdata->process_counter++;
  }
  else if (state == MDRIVER_KERNEL)
  {
      mdata->kernel_counter++;
  }
  else if (state == MDRIVER_STARTUP_PREPARE)
  {
      /* Once we are out of startup, use callout_io_map or callout_memory_map */

      kprintf("I am in STARTUP PREPARE %x\n", mdata->total_message_counter);
      if ((mdata->MYBUS_REGS_POSTKERNEL = (MYBUS_regs_t *)(callout_memory_map(0x10,
                (MBAR_BASE + MYBUS_OFFSET), 
                PROT_READ|PROT_WRITE|PROT_NOCACHE))))
      {
         /* Something bad happened. Disable the interrupt and turn off
            the minidriver */
         mdata->MYBUS_REGS_PREKERNEL->control_register = CTRL_INTERRUPT_OFF;
         return (1);
      }

   }

   /* At this point, we use MYBUS_REGS. We could either be in startup, in 
      kernel loading or at process time. */

   /* Read the interrupt status register immediately upon entry to the handler. */
   StatusReg = mdata->MYBUS_REGS->interrupt_status;

   /* Increase the message counter. */
   mdata->total_message_counter++;

   switch( StatusReg )
   {
      /* Read my data and add to my data area (after data_len in MYBUS_data_t) *.

      /* Make sure that you clear the source of interrupt before you return *.

      /* ... */
   }

   if (state == MDRIVER_STARTUP_FINI)
   {
      val = mdata->total_message_counter;
      kprintf("I am in state STARTUP FINI. Total messages processed=%x \n", val);

      /* Startup has finished.. now I switch over to use the POSTKERNEL mapping */
      mdata->MYBUS_REGS = mdata->MYBUS_REGS_POSTKERNEL;
   }
   return (0);
}