VxWorks BSP学习笔记(基于SBC8349E)
以下结构指出了驱动程序函数的入口点 ../target/h/sioLib.h
Typedef struct sio_drv_funcs /* driver functions */
{
int (*ioctl)
(
SIO_CHAN * pSioChan,
int cmd,
void * arg
);
int (*txStartup)
(
SIO_CHAN * pSioChan
);
int (*callbackInstall)
(
SIO_CHAN * pSioChan,
int callbackType,
STATUS (*callback)(void *, ...),
void * callbackArg
);
int (*pollInput)
(
SIO_CHAN * pSioChan,
char * inChar
);
int (*pollOutput)
(
SIO_CHAN * pSioChan,
char outChar
);
}SIO_DRV_FUNCS;
typedef struct sio_chan /* a serial channel */
{
SIO_DRV_FUNCS * pDrvFuncs;
/* device data */
} SIO_CHAN;
在串行设备的驱动程序中,必须有一个包含指向SIO_DRV_FUNCS指针的结构(XX_CHAN),这个结构可以包含设备的其他信息以及提供给高层协议的回调函数。以16550为例:
../target/h/drv/sio/ns16552Sio.h
typedef struct /* NS16550_CHAN * */
{
/* always goes first */
SIO_DRV_FUNCS * pDrvFuncs; /* driver functions */
/* callbacks */
STATUS (*getTxChar) (); /* pointer to xmitr function */
STATUS (*putRcvChar) (); /* pointer to rcvr function */
void * getTxArg;
void * putRcvArg;
UINT8 *regs; /* NS16552 registers */
UINT8 level; /* 8259a interrupt level for this device */
UINT8 ier; /* copy of ier */
UINT8 lcr; /* copy of lcr, not used by ns16552 driver */
UINT8 mcr; /* copy of modem control register */
UINT16 channelMode; /* such as INT, POLL modes */
UINT16 regDelta; /* register address spacing */
int baudRate;
UINT32 xtal; /* UART clock frequency */
UINT32 options; /* hardware setup options */
} NS16550_CHAN;
串行设备驱动程序的初始化函数应该包含下面的内容:
1) 包含一个指向xx_DRV结构的指针;
2) 初始化xx_CHAN;
3) 初始化驱动程序必要的内容;
4) 重新设置芯片
../target/src/drv/sio/ns16550Sio.c
/* driver functions */
#ifdef INCLUDE_TTY_DEV
static SIO_DRV_FUNCS ns16550SioDrvFuncs =
{
(int (*)()) ns16550Ioctl,
(int (*)()) ns16550TxStartup,
(int (*)()) ns16550CallbackInstall,
(int (*)()) ns16550PollInput,
(int (*)(SIO_CHAN *,char))ns16550PollOutput
};
#else /* INCLUDE_TTY_DEV */
static SIO_DRV_FUNCS ns16550SioDrvFuncs =
{
(int (*)()) NULL,
(int (*)()) NULL,
(int (*)()) NULL,
(int (*)()) ns16550PollInput,
(int (*)(SIO_CHAN *,char))ns16550PollOutput
};
#endif /* INCLUDE_TTY_DEV */
void ns16550DevInit
(
NS16550_CHAN * pChan /* pointer to channel */
)
{
int oldlevel = intLock ();
/* initialize the driver function pointers in the SIO_CHAN's */
pChan->pDrvFuncs = &ns16550SioDrvFuncs;
/* set the non BSP-specific constants */
pChan->getTxChar = ns16550DummyCallback;
pChan->putRcvChar = ns16550DummyCallback;
pChan->channelMode = 0; /* undefined */
pChan->options = (CLOCAL | CREAD | CS8);
pChan->mcr = MCR_OUT2;
/* reset the chip */
ns16550InitChannel (pChan);
intUnlock (oldlevel);
}
SIO_DRV_FUNCS结构中的各函数的实现
sysDuartHwInit(),由usrInit()函数调用,初始化了驱动程序相关的xx_CHAN结构。调用了xxDevInit()函数初始化硬件,设置中断未连接标志。该函数在内核初始化之前完成。使用16550芯片的例子:
void sysDuartHwInit (void)
{
int i;
eumbbar_base = (char *)CCSBAR;
for (i = 0; i < N_DUART_CHANNELS; i++)
{
ns16550Chan[i].regs = (UINT8 *)devDuartParas[i].baseAdrs;
ns16550Chan[i].level = devDuartParas[i].intLevel;
ns16550Chan[i].channelMode = SIO_MODE_INT;
ns16550Chan[i].regDelta = devDuartParas[i].regSpace;
ns16550Chan[i].baudRate = DUART_BAUD;
ns16550Chan[i].xtal = sysClkFreqGet();
ns16550DevInit (&ns16550Chan[i]);
}
/* 以下初始硬件 */
if (ns16550Chan[0].channelMode == SIO_MODE_INT)
{
eumbbar_base[UDCR1] = 0x01; /* set duart mode */
eumbbar_base[ULCR1] = 0x80; /* open DLAB */
eumbbar_base[UAFR1] = 0x00;
eumbbar_base[UDMB1] = 0x03; /* for mpc8245 MSB, 9600bps @66Mhz */
eumbbar_base[UDLB1] = 0x64; /* LSB */
eumbbar_base[ULCR1] = 0x03; /* clear DLAB, no-parity, 1stop bit, 8bit data */
eumbbar_base[UMCR1] = 0x02; /* disable loopback mode */
eumbbar_base[UIER1] = 0x03; /* Tx empty, Rx interrupt enable */
}
if (ns16550Chan[1].channelMode == SIO_MODE_INT)
{
eumbbar_base[UDCR2] = 0x01; /* set duart mode */
eumbbar_base[ULCR2] = 0x80; /* open DLAB */
eumbbar_base[UAFR2] = 0x00;
eumbbar_base[UDMB2] = 0x01; /* for mpc8245 MSB, 9600bps @66Mhz */
eumbbar_base[UDLB2] = 0xB2; /* LSB */
eumbbar_base[ULCR2] = 0x03; /* clear DLAB, no-parity, 1stop bit, 8bit data */
eumbbar_base[UMCR2] = 0x02; /* diable loopback mode */
eumbbar_base[UIER2] = 0x03; /* Tx empty, Rx interrupt enable */
}
} /* sysDuartHwInit () */
sysDuartHwInit2()由usrRoot任务中的sysClkConnnect()函数调用,安装了设备的中断服务程序。这个函数在系统初始化之后执行,此时系统已经可以执行中断连接了。
void sysSerialHwInit2 (void)
{
int i;
/* connect serial interrupts */
for (i = 0; i < N_DUART_CHANNELS; i++)
{
(void) intConnect ((VOIDFUNCPTR *)((int)devDuartParas[i].vector),
(VOIDFUNCPTR)ns16550Int, (int)&ns16550Chan[i] );
intEnable (devDuartParas[i].vector);
}
} /* sysDuartHwInit2 () */
sysSerialChanGet()的作用是将串行设备通道的索引转换为该通道的设备描述符。通常目标机代理会使用这个函数以获得通道的指针,usrRoot()任务也会用到该函数。
SIO_CHAN * sysSerialChanGet
(
int channel /* serial channel */
)
{
if ( (channel < 0) ||
(channel >= (int)NELEMENTS(sysSerialSioChans)) )
return (SIO_CHAN *) ERROR;
return sysSerialSioChans[channel];
}