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 this:
struct mserial_data { uintptr_t port; uintptr_t port_k; uint16_t intr_calls; uint16_t ncalls; uint16_t errors; uint16_t err; uint16_t last_count; uint16_t data_len; }; void Delay(int n) { unsigned int i; for(i=0;i<n;i++); } uintptr_t init_hw() { int cnt; uint64_t base = SH_SCIF_BASE; int baud = 14400; int clk = 33333333; unsigned port; if ((port = startup_io_map(120, base)) == NULL) return (NULL); cnt = clk / (baud * 64 / 2) - 1; // assumes SCSMR1.CKS = 0 // set clock selection out16(port + SH_SCIF_SCSCR_OFF, 0x0); Delay(1); // Enable reset state of the FIFO register out16(port + SH_SCIF_SCFCR_OFF, SH_SCIF_SCFCR_TFRST | SH_SCIF_SCFCR_RFRST); // data transfer format // 8 bits no parity // CKS = 0 out16(port + SH_SCIF_SCSMR_OFF,0); Delay(1); // baud rate out8(port + SH_SCIF_SCBRR_OFF,cnt); Delay(1); //Disable the reset state of the fifo set_port16(port + SH_SCIF_SCFCR_OFF, SH_SCIF_SCFCR_TFRST | SH_SCIF_SCFCR_RFRST, 0); // FIFO control out16(port + SH_SCIF_SCFCR_OFF + 4, 0x0); Delay(1); out8(port + SH_SCIF_SCFTDR_OFF, 0); Delay(1); out16(port + SH_SCIF_SCSPTR_OFF + 4, 0x41); out16(port + SH_SCIF_SCFCR_OFF, 0x30 | 0x200 | SH_SCIF_SCFCR_MCE); out16(port + SH_SCIF_SCSCR_OFF, SH_SCIF_SCSCR_RE | SH_SCIF_SCSCR_TE | SH_SCIF_SCSCR_TIE | SH_SCIF_SCSCR_RIE); Delay(1); out16(port + SH7760_SCIF_SCSPTR_OFF, SH_SCIF_SCSPTR_RTSIO); out16(port + SH_SCIF_SCSCR_OFF, SH_SCIF_SCSCR_RE | SH_SCIF_SCSCR_RIE); return (port); } int mini_serial(int state, void *data) { uint8_t *bp = 0; uint16_t count; uint8_t *dptr, c; struct mserial_data *mdata; int num, i; int pending_interrupts; mdata = (struct mserial_data *) data; dptr = (uint8_t *) (mdata + 1); if (state == MDRIVER_INTR_ATTACH) { /* disable the serial interrupt */ set_port16(mdata->port + SH_SCIF_SCSCR_OFF, SH_SCIF_SCSCR_RE | SH_SCIF_SCSCR_RIE, 0); return (1); } else if (state == MDRIVER_INIT) { /* the first time called initialize the hardware and do data setup */ mdata->port = init_hw(); if (mdata->port == 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->port_k = callout_io_map(0x120, SH_SCIF_BASE)) == 0) { /* something bad so disable the driver */ set_port16(mdata->port + SH_SCIF_SCSCR_OFF, SH_SCIF_SCSCR_RE | SH_SCIF_SCSCR_RIE, 0); 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; } /* get the data from the serial port and clear any errors */ // Handle the error messages: Overrun, Framing, Parity pending_interrupts = in16(mdata->port + SH_SCIF_SCFSR_OFF); if (in8(mdata->port + SH7760_SCIF_SCLSR_OFF) & SH_SCIF_SCLSR_ORER) { set_port16(mdata->port + SH7760_SCIF_SCLSR_OFF, SH_SCIF_SCLSR_ORER, 0); } if ((pending_interrupts & SH_SCIF_SCFSR_PER)) { set_port16(mdata->port + SH_SCIF_SCFSR_OFF, SH_SCIF_SCFSR_PER | SH_SCIF_SCFSR_ER | SH_SCIF_SCFSR_DR, 0); } if (pending_interrupts & SH_SCIF_SCFSR_FER) { set_port16(mdata->port + SH_SCIF_SCFSR_OFF, SH_SCIF_SCFSR_FER | SH_SCIF_SCFSR_ER | SH_SCIF_SCFSR_DR, 0); } if (pending_interrupts & SH_SCIF_SCFSR_BRK ) { set_port16(mdata->port + SH_SCIF_SCFSR_OFF, SH_SCIF_SCFSR_BRK | SH_SCIF_SCFSR_DR, 0); } /* grab data */ if (in16(mdata->port + SH7760_SCIF_SCRFDR_OFF) ) { c = in8(mdata->port + SH_SCIF_SCFRDR_OFF); set_port16(mdata->port + SH_SCIF_SCFSR_OFF, SH_SCIF_SCFSR_RDF, 0); dptr[mdata->data_len] = c; mdata->data_len = mdata->data_len + 1; } if (pending_interrupts & (SH_SCIF_SCFSR_TDFE|SH_SCIF_SCFSR_TEND) ) { set_port16(mdata->port + SH_SCIF_SCSCR_OFF, SH_SCIF_SCSCR_TIE,0); } //Clear all the interrupts set_port16(mdata->port + SH7760_SCIF_SCLSR_OFF, SH_SCIF_SCLSR_ORER, 0); set_port16(mdata->port + SH_SCIF_SCFSR_OFF, 0xff, 0); if (state == MDRIVER_STARTUP_FINI) mdata->port = mdata->port_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.
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_io_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_io_map() is called and the pointer is stored in the data area, however the startup_io_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_io_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.