if_cs8900a.c
来自「开放源码实时操作系统源码.」· C语言 代码 · 共 751 行 · 第 1/2 页
C
751 行
esa[3], esa[4], esa[5] );
#if !defined(CYGSEM_DEVS_ETH_CL_CS8900A_WRITE_EEPROM) || !defined(CS8900A_PROGRAM_EEPROM)
diag_printf("*** PERMANENT EEPROM WRITE NOT ENABLED ***\n");
#endif
#endif // DEBUG
// We can write the MAC address into the interface info,
// and the chip registers no problem.
for ( i = 0; i < sizeof(cpd->esa); i++ )
cpd->esa[i] = esa[i];
for (i = 0; i < sizeof(cpd->esa); i += 2) {
cyg_uint16 reg = cpd->esa[i] | (cpd->esa[i+1] << 8);
put_reg(cpd->base, PP_IA+i, reg );
}
#if defined(CYGSEM_DEVS_ETH_CL_CS8900A_WRITE_EEPROM) && defined(CS8900A_PROGRAM_EEPROM)
if (CS8900A_PROGRAM_EEPROM(cpd))
return 1;
else
return 0;
#elif defined(CYGSEM_DEVS_ETH_CL_CS8900A_WRITE_EEPROM) && !defined(CS8900A_PROGRAM_EEPROM)
/* WRITE_EEPROM requested, but no PROGRAM_EEPROM provided */
return 1;
#else /* !CYGSEM_DEVS_ETH_CL_CS8900A_WRITE_EEPROM - No need to write EEPROM */
return 0;
#endif
#ifdef ETH_DRV_GET_MAC_ADDRESS
case ETH_DRV_GET_MAC_ADDRESS:
// Extract the MAC address that is in the chip, and tell the
// system about it.
for (i = 0; i < sizeof(cpd->esa); i += 2) {
unsigned short z = get_reg(cpd->base, PP_IA+i/2 );
esa[i] = (unsigned char)(0xff & z);
esa[i+1] = (unsigned char)(0xff & (z >> 8));
}
return 0;
#endif
case ETH_DRV_SET_MC_LIST:
case ETH_DRV_SET_MC_ALL:
// Note: this code always accepts all multicast addresses if any
// are desired. It would be possible to accept a subset by adjusting
// the Logical Address Filter (LAF), but that would require scanning
// this list and building a suitable mask.
if (mc_list->len) {
cpd->rxmode |= PP_RxCTL_Multicast;
} else {
cpd->rxmode &= ~PP_RxCTL_Multicast;
}
put_reg(base, PP_RxCTL, cpd->rxmode); // When is it safe to do this?
return 0;
default:
return 1;
break;
}
}
// This routine is called to see if it is possible to send another packet.
// It will return non-zero if a transmit is possible, zero otherwise.
static int
cs8900a_can_send(struct eth_drv_sc *sc)
{
cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
cyg_addrword_t base = cpd->base;
cyg_uint16 stat;
stat = get_reg(base, PP_LineStat);
if ((stat & PP_LineStat_LinkOK) == 0) {
return false; // Link not connected
}
#ifdef CYGPKG_KERNEL
// Horrible hack!
if (cpd->txbusy) {
cyg_tick_count_t now = cyg_current_time();
if ((now - cpd->txstart) > 25) {
// 250ms is more than enough to transmit one frame
#if DEBUG & 1
diag_printf("CS8900: Tx interrupt lost\n");
#endif
cpd->txbusy = false;
// Free up the buffer (with error indication)
(sc->funs->eth_drv->tx_done)(sc, cpd->txkey, 1);
}
}
#endif
return (cpd->txbusy == false);
}
// This routine is called to send data to the hardware.
static void
cs8900a_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len,
int total_len, unsigned long key)
{
cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
cyg_addrword_t base = cpd->base;
int i;
int len;
cyg_uint8 *data;
cyg_uint16 saved_data = 0, *sdata;
cyg_uint16 stat;
bool odd_byte = false;
// Mark xmitter busy
cpd->txbusy = true;
cpd->txkey = key;
#ifdef CYGPKG_KERNEL
cpd->txstart = cyg_current_time();
#endif
// Start the xmit sequence
#ifdef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED
total_len = CYG_SWAP16(total_len);
#endif
// The hardware indicates that there are options as to when the actual
// packet transmission will start wrt moving of data into the transmit
// buffer. However, impirical results seem to indicate that if the
// packet is large and transmission is allowed to start before the
// entire packet has been pushed into the buffer, the hardware gets
// confused and the packet is lost, along with a "lost" Tx interrupt.
// This may be a case of the copy loop below being interrupted, e.g.
// a system timer interrupt, and the hardware getting unhappy that
// not all of the data was provided before the transmission should
// have completed (i.e. buffer underrun).
// For now, the solution is to not allow this overlap.
//HAL_WRITE_UINT16(cpd->base+CS8900A_TxCMD, PP_TxCmd_TxStart_5)
// Start only when all data sent to chip
HAL_WRITE_UINT16(cpd->base+CS8900A_TxCMD, PP_TxCmd_TxStart_Full);
HAL_WRITE_UINT16(cpd->base+CS8900A_TxLEN, total_len);
// Wait for controller ready signal
{
// add timeout per cs8900a bugzilla report 1000281 */
int timeout = 1000;
do {
stat = get_reg(base, PP_BusStat);
#if DEBUG & 1
if( stat & PP_BusStat_TxBid )
diag_printf( "cs8900a_send: Bid error!\n" );
#endif
} while (!(stat & PP_BusStat_TxRDY) && --timeout);
if( !timeout ) {
// we might as well just return, since if we write the data it will
// just get thrown away
return;
}
}
// Put data into buffer
for (i = 0; i < sg_len; i++) {
data = (cyg_uint8 *)sg_list[i].buf;
len = sg_list[i].len;
if (len > 0) {
/* Finish the last word. */
if (odd_byte) {
// This new byte must get on the bus _after_ the last saved odd byte, it therefore
// belongs in the MSB of the CS8900a
#ifdef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED
saved_data |= *data++;
#else
saved_data |= ((cyg_uint16)*data++) << 8;
#endif
HAL_WRITE_UINT16(cpd->base+CS8900A_RTDATA, saved_data);
len--;
odd_byte = false;
}
if (((CYG_ADDRESS)data & 0x1) == 0) {
/* Aligned on 16-bit boundary, so output contiguous words. */
sdata = (cyg_uint16 *)data;
while (len > 1) {
// Make sure data get on the bus in Big Endian format
#if((CYG_BYTEORDER == CYG_MSBFIRST) && defined(CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED) || \
(CYG_BYTEORDER == CYG_LSBFIRST) && !defined(CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED ))
HAL_WRITE_UINT16(cpd->base+CS8900A_RTDATA, *sdata++);
#else
HAL_WRITE_UINT16(cpd->base+CS8900A_RTDATA, CYG_SWAP16(*sdata++));
#endif
len -= sizeof(cyg_uint16);
}
data = (cyg_uint8 *)sdata;
} else {
/* Not 16-bit aligned, so byte copy */
while (len > 1) {
saved_data = (cyg_uint16)*data++; // reuse saved_data
// Make sure data get on the bus in Big Endian format, the first byte belongs in the
// LSB of the CS8900A
#ifdef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED
saved_data = ((cyg_uint16)*data++) | (saved_data << 8);
#else
saved_data |= ((cyg_uint16)*data++) << 8;
#endif
HAL_WRITE_UINT16(cpd->base+CS8900A_RTDATA, saved_data);
len -= sizeof(cyg_uint16);
}
}
/* Save last byte, if necessary. */
if (len == 1) {
saved_data = (cyg_uint16)*data;
// This _last_ byte must get on the bus _first_, it therefore belongs in the LSB of
// the CS8900a
#ifdef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED
saved_data = (saved_data << 8);
#endif
odd_byte = true;
}
}
}
if (odd_byte) {
HAL_WRITE_UINT16(cpd->base+CS8900A_RTDATA, saved_data);
}
}
// This function is called when a packet has been received. It's job is
// to prepare to unload the packet from the hardware. Once the length of
// the packet is known, the upper layer of the driver can be told. When
// the upper layer is ready to unload the packet, the internal function
// 'cs8900a_recv' will be called to actually fetch it from the hardware.
static void
cs8900a_RxEvent(struct eth_drv_sc *sc, int stat)
{
cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
cyg_addrword_t base = cpd->base;
cyg_uint16 len;
if(stat & PP_RxCFG_RxOK) {
// Only start reading a message if one has been received
HAL_READ_UINT16(base+CS8900A_RTDATA, stat);
HAL_READ_UINT16(base+CS8900A_RTDATA, len);
#ifdef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED
len = CYG_SWAP16(len);
#endif
CYG_ASSERT(len > 0, "Zero length ethernet frame received");
#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
if (cyg_io_eth_net_debug) {
diag_printf("RxEvent - stat: %x, len: %d\n", stat, len);
}
#endif
(sc->funs->eth_drv->recv)(sc, len);
}
}
// This function is called as a result of the "eth_drv_recv()" call above.
// It's job is to actually fetch data for a packet from the hardware once
// memory buffers have been allocated for the packet. Note that the buffers
// may come in pieces, using a scatter-gather list. This allows for more
// efficient processing in the upper layers of the stack.
static void
cs8900a_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len)
{
cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
cyg_addrword_t base = cpd->base;
int i, mlen;
cyg_uint16 *data, val;
cyg_uint8 *cp, cval;
for (i = 0; i < sg_len; i++) {
data = (cyg_uint16 *)sg_list[i].buf;
mlen = sg_list[i].len;
while (mlen >= sizeof(*data)) {
HAL_READ_UINT16(base+CS8900A_RTDATA, val);
if (data) {
#if((CYG_BYTEORDER == CYG_MSBFIRST) && defined(CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED) || \
(CYG_BYTEORDER == CYG_LSBFIRST) && !defined(CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED ))
*data++ = val;
#else
*data++ = CYG_SWAP16(val);
#endif
}
mlen -= sizeof(*data);
}
if (mlen) {
HAL_READ_UINT16(base+CS8900A_RTDATA, val);
#ifndef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED
// last odd byte will be in the LSB
cval = (cyg_uint8)(val);
#elif(CYG_BYTEORDER == CYG_MSBFIRST)
// last odd byte will be in the MSB
cval = (cyg_uint8)(val >> 8);
#endif
cval &= 0xff;
if ((cp = (cyg_uint8 *)data) != 0) {
*cp = cval;
}
}
}
}
static void
cs8900a_TxEvent(struct eth_drv_sc *sc, int stat)
{
cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
cyg_addrword_t base = cpd->base;
stat = get_reg(base, PP_TER);
#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
if (cyg_io_eth_net_debug) {
diag_printf("Tx event: %x\n", stat);
}
#endif
cpd->txbusy = false;
(sc->funs->eth_drv->tx_done)(sc, cpd->txkey, 0);
}
static void
cs8900a_BufEvent(struct eth_drv_sc *sc, int stat)
{
if (stat & PP_BufCFG_RxMiss) {
}
if (stat & PP_BufCFG_TxUE) {
}
}
static void
cs8900a_poll(struct eth_drv_sc *sc)
{
cyg_uint16 event;
cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
cyg_addrword_t base = cpd->base;
HAL_READ_UINT16(base+CS8900A_ISQ, event);
while (event != 0) {
switch (event & ISQ_EventMask) {
case ISQ_RxEvent:
cs8900a_RxEvent(sc, event);
break;
case ISQ_TxEvent:
cs8900a_TxEvent(sc, event);
break;
case ISQ_BufEvent:
cs8900a_BufEvent(sc, event);
break;
case ISQ_RxMissEvent:
// Receive miss counter has overflowed
break;
case ISQ_TxColEvent:
// Transmit collision counter has overflowed
break;
default:
#if DEBUG & 1
diag_printf("%s: Unknown event: %x\n", __FUNCTION__, event);
#endif
break;
}
HAL_READ_UINT16(base+CS8900A_ISQ, event);
}
CYGHWR_CL_CS8900A_PLF_INT_CLEAR(cpd);
}
#ifdef CYGSEM_DEVS_ETH_CL_CS8900A_NOINTS
void
cs8900a_fake_int(cyg_addrword_t param)
{
struct eth_drv_sc *sc = (struct eth_drv_sc *) param;
int s;
#if DEBUG & 1
diag_printf("cs8900a_fake_int()\n");
#endif
while (true) {
cyg_thread_delay(5);
s = splnet();
cs8900a_poll(sc);
splx(s);
}
}
#endif
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?