The following example shows how to interact with hardware from within a minidriver.The important message is that the mapping of hardware registers is different, depending on where in the boot process that the minidriver is called. This transition is handled in the MDRIVER_STARTUP_PREPARE and the MDRIVER_STARTUP_FINI stages.
/------------------------------------------------------------------------/ Here is a more complex example of a mini driver for a fictional hardware device called 'MYBUS'. Characteristics of MYBUS are as follows: - series of 8 bit registers (status and data) at address 0xFF000000 (MBAR) - interrupt 56 is generated when a character arrives at the MYBUS port There are registers at this address which will be read/written to. /------------------------------------------------------------------------/ #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; /************** Mini-driver data area ************/ typedef struct { MYBUS_regs *MYBUS_REGS; //this value will either be //same as PREKERNEL or POSTKERNEL MYBUS_regs *MYBUS_REGS_PREKERNEL_START; //register mappings to //use before kernel starts MYBUS_regs *MYBUS_REGS_POSTKERNEL_START; //register mappings to use //after kernel starts U16 total_message_counter; //total times mini-handler //is called U16 process_counter; //times called after kernel //is running U16 kernel_counter; //times called during //kernel 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 only called once when the mini-driver 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 .. //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 .. mini-driver 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; mdata->total_message_counter = 0; // Initialize messages received 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 mini-driver */ 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 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); }