The minidriver handler function

The prototype for the minidriver handler function is as follows:

int mdriver_handler(int state, void *data);

The state value informs the handler function where in the boot process it's being called from. See the mdriver_add() documentation in the API and Datatypes chapter of this guide. The data parameter is a virtual pointer to the minidriver's allocated data area. For this sample driver, the source code for the handler function would look like:

struct mserial_data
{
   psc_regs	*psc;
   psc_regs	*psc_k;
   uint16_t	intr_calls;
   uint16_t	ncalls;
   uint16_t	errors;
   uint16_t	last_count;
   uint16_t 	data_len;
};

/*
 * Hardware initialization function which only gets called 
 * the first time the mini-driver is run.
 * The Baud, Clk and port are hardcoded for this example
 *   baud = 14400
 *   clk = 132000000
 *   port = PSC1
 */
static psc_regs *
init_hw()
{
   /* hardcode to PSC1, baud and clk */
   int 		baud = 14400;
   int 		clk = 132000000;
   paddr64_t	psc_base = MGT5200_MBAR_BASE + 0x2000;
   psc_regs		*psc;
   gpio_std_regs	*gpio;

   if ((psc = (psc_regs *)(startup_memory_map(0xA0,psc_base, 
 	  PROT_READ|PROT_WRITE|PROT_NOCACHE))) == 0)
       return (0);
		
   if ((gpio = (gpio_std_regs*)(startup_memory_map(0x40,(MGT5200_MBAR_BASE + 0x0b00),
 	  PROT_READ|PROT_WRITE|PROT_NOCACHE))) == 0)
   {
      startup_memory_unmap((void *)psc);
      return (0);
   }

   baud = ( (clk / baud) + 16 ) / 32 ; /* calculate baud rate */
   psc->CR = PSC_CR_TXD | PSC_CR_RXD;
   psc->SICR = 0x00000000;         /* write to SICR: SIM2 = uart mode,dcd 
                                   does not affect rx */
   psc->SR_CSR = 0xdd00;           /* write to CSR: RX/TX baud rate 
                                   from timers */
   psc->CTUR = (baud >> 8) & 0xff; /* write to CTUR: divide counter 
                                   upper byte */
   psc->CTLR = baud & 0xff;        /* write to CTLR: divide counter 
                                   lower byte */
   psc->CR = 0x20;                 /* write to CR: reset RX and 
                                   RXFIFO */
   psc->CR = 0x30;                 /* write to CR: reset TX and 
                                   TXFIFO */
   psc->CR = 0x40;                 /* write to CR: reset 
                                   error status */
   psc->CR = 0x50;                 /* write to CR: reset 
                                   break change int */
   psc->CR = 0x10;                 /* write to CR: reset MR pointer */
   psc->IPCR_ACR = 0x00;           /* write to ACR: disable state 
                                   change CTS/ DCD interrupts */
   psc->ModeReg = 0x73;            /* write to MR1: 8bit data, 
                                   no parity */
   psc->ModeReg = 0x07;            /* write to MR2: normal 
                                   mode,1stop bit */
   psc->OP1 = 1;                   /* Write to OP1: enable 
                                   RTS to send */
   psc->ISR_IMR = 0x0000;          /* write to IMR: Mask RxRDY and 
                                   TxRDY from causing an interrupt */
   psc->RFALARM = 511;
   psc->TFALARM = 0xF8;
   psc->RFCNTL = 0x04;
	
   gpio->port_config = (gpio->port_config & GPIO_PSC1_MASK) | 
                       GPIO_PSC1_UART_ENABLE;
		
   /* Enable the transmitters and receivers */
   psc->CR = 0x05; /* write to CR: enable TX and RX.*/
   psc->ISR_IMR = PSC_ISR_RxRDY;

   startup_memory_unmap((void *)gpio);
	
   /* return mapped register pointer */
   return (psc);
}

int mini_serial(int state, void *data)
{
   uint8_t	   	*bp = 0;
   paddr64_t	psc_base = MGT5200_MBAR_BASE + 0x2000;
   uint16_t	count;
   uint8_t		*dptr;
   struct mserial_data	*mdata;
	
   mdata = (struct mserial_data *) data;
   dptr = (uint8_t *) (mdata + 1);

   if (state == MDRIVER_INTR_ATTACH)
   {
	  /* disable the serial interrupt */
	  mdata->psc->ISR_IMR = 0x0000;
	  * make sure there are no characters in the hardware fifo */
	  count = 0;
	  while (mdata->psc->SR_CSR & PSC_SR_RxRDY) 
	  {
	      bp = (uint8_t*) &mdata->psc->RB_TB;
	      dptr[mdata->data_len+count] = *bp;
	      count++;
	  }
	      mdata->last_count = count;
	      count = count + mdata->data_len;
 	      mdata->data_len = count;
	
	      /* reset any error conditions */    
	      if ((mdata->psc->SR_CSR & PSC_SR_ORERR) ||
	     	    (mdata->psc->SR_CSR & PSC_SR_RB) ||
	    	    (mdata->psc->SR_CSR & PSC_SR_FE) ||
		    (mdata->psc->SR_CSR & PSC_SR_PE))
	      {
		    mdata->psc->CR = PSC_CR_RESET_ERR;
		    count = mdata->errors + 1;
		    mdata->errors = count;
	      }
	
	      return (1);
    }
    else if (state == MDRIVER_INIT)
    {
       /* the first time called initialize the hardware and do data setup */
       mdata->psc = init_hw();
       if (mdata->psc == 0)
	  return (1);
       mdata->intr_calls = 0;
       mdata->ncalls = 0;
       mdata->errors = 0;
       mdata->data_len = 0;
    }
    else if (state == MDRIVER_STARTUP_PREPARE)
    {
       /* once we are out of startup use callout_io_map */
       if ((mdata->psc_k = (psc_regs *)(callout_memory_map(0xA0,psc_base,
    	   PROT_READ|PROT_WRITE|PROT_NOCACHE))) == 0)
       {
	   /* something bad so disable the driver */
	   mdata->psc->ISR_IMR = 0x0000;
	   return (1);
	   }
       }

    /* count the number of times the mini-driver is called */
    count = mdata->ncalls + 1;
    mdata->ncalls = count;
    if (state == MDRIVER_PROCESS)
    {
	/* called because of an interrupt */
	count = mdata->intr_calls + 1;
	mdata->intr_calls = count;
    }	
	
    count = 0;
    while (mdata->psc->SR_CSR & PSC_SR_RxRDY) 
    {
	bp = (uint8_t*) &mdata->psc->RB_TB;
    	dptr[mdata->data_len+count] = *bp;
	count++;
     }
    	
     mdata->last_count = count;
     count = count + mdata->data_len;
     mdata->data_len = count;

     /* reset any error conditions */    
     if ((mdata->psc->SR_CSR & PSC_SR_ORERR) ||
     	 (mdata->psc->SR_CSR & PSC_SR_RB) ||
    	 (mdata->psc->SR_CSR & PSC_SR_FE) ||
	 (mdata->psc->SR_CSR & PSC_SR_PE))
     {
	 mdata->psc->CR = PSC_CR_RESET_ERR;
	 count = mdata->errors + 1;
	 mdata->errors = count;
     }

     if (state == MDRIVER_STARTUP_FINI)
	mdata->psc = mdata->psc_k;	
	
     return (0);
}

In this example, the handler function stores call information, so a structure has been created to allow easier access to the data area. The data area is filled with the received characters.

Access to the serial port registers is necessary. At initialization time, the registers are mapped with startup_memory_map(), and the pointer is stored in the data area during the startup phase. The registers are mapped in with startup_memory_map(). Once the startup phase is complete, the handler must use callout_memory_map() to access the registers.

During the MDRIVER_INIT state, the data area is initialized, and the serial hardware is set up; this is called only once. At this time, the startup_memory_map() is called to map in the hardware registers, and the pointer is stored in the data area. This pointer is used for hardware access until the handler is called with a state of STARTUP_MDRIVER_PREPARE. At this time callout_memory_map() can be called and the pointer is stored in the data area, however the startup_memory_map() pointer should still be used. Once the handler is called with a state of MDRIVER_STARTUP_FINI, the handler starts using the pointer returned by callout_memory_map() for all hardware access. This pointer is then used for all further invocations of the minidriver handler.

When the handler is called with a state of MDRIVER_INTR_ATTACH, it disables the device interrupt and returns a value of 1 requesting an exit of the handler.