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:
#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); }