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:
- There's a series of 8-bit registers (status and data) at address 0xFF000000 (MBAR_BASE).
- Interrupt 56 is generated when a character arrives at the MYBUS port.
- There are registers at this address which will be read from and written to.
 |
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);
}