📄 cs8900if.c
字号:
#if ETH_PAD_SIZE
/* drop the padding word */
pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
#endif
for (q = p; q != 0; q = q->next)
{
LWIP_DEBUGF(NETIF_DEBUG, ("cs8900_input: pbuf @%p tot_len %"U16_F" len %"U16_F"\n", q, q->tot_len, q->len));
/* read 8 bytes per iteration */
ptr = q->payload;
i = q->len / 8;
while(i > 0)
{
*ptr = *rxtx_reg;
ptr++;
*ptr = *rxtx_reg;
ptr++;
*ptr = *rxtx_reg;
ptr++;
*ptr = *rxtx_reg;
ptr++;
i--;
}
/* read remainder */
i = ((q->len % 8) + 1) / 2;
while(i > 0)
{
*ptr = *rxtx_reg;
ptr++;
i--;
}
}
#if ETH_PAD_SIZE
/* reclaim the padding word */
pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endif
}
// could not allocate a pbuf
else
{
// skip received frame
// TODO: maybe do not skip the frame at this point in time?
PACKETPP = CS_PP_RXCFG;
PPDATA = (0x0003U | 0x0100U/*RxOKiE*/ | 0x0040U/*Skip_1*/);
#if (CS8900_STATS > 0)
((struct cs8900if *)netif->state)->dropped++;
#endif
snmp_inc_ifindiscards(netif);
len = 0;
}
}
// length was zero
else
{
}
}
return p;
}
/**
* To be called when the cs8900a needs service. Does
* not assume the cs8900a needs service. Does test the
* cs8900a whether it needs service.
*
* As such, may be used robustly called as a deferred
* (or "late") interrupt handler, or may be called in
* a loop to implement polling, or both.
*
* Use cs8900if_service() from your application instead
* of this function.
*/
static void cs8900_service(struct netif *netif)
{
u8_t events2service = CS8900_EVTS2SRV;
#if (CS8900_STATS > 0)
u16_t miss_count = 0, coll_count = 0;
#endif
// NOTES:
// static, so only initialized to zero at program start.
// irq_status will always hold the last ISQ event register that
// still needs service. As such, we may leave this function if
// we encounter an event we cannot service yet, and return later
// to try to service it.
static u16_t irq_status = 0x0000U;
// The "cs8900_needs_service" flag indicates whether any events
// still need to be serviced.
// clear flag here.
// a receive interrupt can, *concurrently with this function*,
// set this flag on new ISQ event occurences.
// we will re-evaluate the correct setting of this flag at
// function exit (below).
((struct cs8900if *)netif->state)->needs_service = 0;
#ifdef LED_NEED_SERVICE
leds_off(LED_NEED_SERVICE);
#endif
/* no unhandled irq_status left? */
if (irq_status == 0x0000U)
{
/* read ISQ register */
irq_status = ISQ;
}
/* ISQ interrupt event, and allowed to service in this loop? */
while ((irq_status != 0x0000U) && (events2service-- > 0))
{
/* investigate event */
if ((irq_status & 0x003fU) == 0x0004U/*Receiver Event*/)
{
/* correctly received frame, either broadcast or individual address */
/* TODO: think where these checks should appear: here or in cs8900_input() */
if ((irq_status & 0x0100U/*RxOK*/) && (irq_status & 0x0c00U/*Broadcast | Individual*/))
{
/* read the frame from the cs8900a */
cs8900if_input(netif);
}
else
{
/* skip this frame */
PACKETPP = CS_PP_RXCFG;
PPDATA |= 0x0040U/*Skip_1*/;
#if (CS8900_STATS > 0)
((struct cs8900if *)netif->state)->dropped++;
#endif
}
}
#if (CS8900_STATS > 0)
else if ((irq_status & 0x003fU) == 0x0010U/*RxMISS Event*/)
{
miss_count += (irq_status >> 6);
}
else if ((irq_status & 0x003fU) == 0x0012U/*TxCOL Event*/)
{
coll_count += (irq_status >> 6);
}
#endif
/* read ISQ register */
irq_status = ISQ;
}
/* we did not deplete the ISQ? */
if (irq_status != 0x0000U)
{
/* the cs8900a still needs service */
((struct cs8900if *)netif->state)->needs_service = 1;
#ifdef LED_NEED_SERVICE
leds_on(LED_NEED_SERVICE);
#endif
}
#if (CS8900_STATS > 1) /* follow misses and collisions on a per-packet basis? */
/* read RxMiss Counter (zeroes itself upon read) */
PACKETPP = CS_PP_RXMISS;
miss_count += (PPDATA >> 6);
/* read RxCol Counter (zeroes itself upon read) */
PACKETPP = CS_PP_TXCOL;
coll_count += (PPDATA >> 6);
#endif
#if (CS8900_STATS > 0)
/* copy statistics counters into netif state fields */
((struct cs8900if *)netif->state)->missed += miss_count;
if (miss_count > 0) LWIP_DEBUGF(NETIF_DEBUG | 1, ("cs8900_input: %"U16_F" missed packets due to rx buffer overrun\n", miss_count));
((struct cs8900if *)netif->state)->collisions += coll_count;
if (coll_count > 0) LWIP_DEBUGF(NETIF_DEBUG | 1, ("cs8900_input: %"U16_F" packet collisions\n", coll_count));
#endif
}
/**
* Service the CS8900.
*
* Can be called in a polling manner, or only after the CS8900 has raised
* an interrupt request.
*
* @param netif The lwIP network interface data structure belonging to this device.
*
*/
void cs8900if_service(struct netif *netif)
{
// is there a reason to call the service routine?
if ((((struct cs8900if *)netif->state)->needs_service) ||
(((struct cs8900if *)netif->state)->use_polling))
{
cs8900_service(netif);
}
}
/**
* Read a received packet from the CS8900.
*
* This function should be called when a packet is received by the CS8900
* and is fully available to read. It moves the received packet to a pbuf
* which is forwarded to the IP network layer or ARP module. It transmits
* a resulting ARP reply or queued packet.
*
* @param netif The lwIP network interface to read from.
*
* @internal Uses cs8900_input() to move the packet from the CS8900 to a
* newly allocated pbuf.
*
*/
void cs8900if_input(struct netif *netif)
{
struct eth_hdr *ethhdr = NULL;
struct pbuf *p = NULL;
/* move received packet into a new pbuf */
p = cs8900_input(netif);
/* no packet could be read */
if (p == NULL) {
/* silently ignore this */
return;
}
/* points to packet payload, which starts with an Ethernet header */
ethhdr = p->payload;
switch (htons(ethhdr->type)) {
/* IP packet? */
case ETHTYPE_IP:
#if 0
/* CSi disabled ARP table update on ingress IP packets.
This seems to work but needs thorough testing. */
/* update ARP table */
etharp_ip_input(netif, p);
#endif
/* skip Ethernet header */
pbuf_header(p, -(s16_t)sizeof(struct eth_hdr));
LWIP_DEBUGF(NETIF_DEBUG, ("cs8900_input: passing packet up to IP\n"));
/* pass to network layer */
netif->input(p, netif);
break;
/* ARP packet? */
case ETHTYPE_ARP:
/* pass p to ARP module */
etharp_arp_input(netif, (struct eth_addr *)&netif->hwaddr, p);
break;
/* unsupported Ethernet packet type */
default:
/* free pbuf */
pbuf_free(p);
p = NULL;
break;
}
}
/**
* Reset the CS8900 Ethernet MAC/PHY chip.
*
* @param netif The lwIP network interface data structure belonging to this device.
* MAY be NULL as we do not support multiple devices yet.
* @note You SHOULD call cs8900if_init() afterwards to
* initialize and configure the chip.
*/
void cs8900if_reset(struct netif *netif)
{
/* reset the cs8900a chip */
cs8900_reset(netif);
}
/**
* Initialize the CS8900 Ethernet MAC/PHY and its device driver.
*
* @param netif The lwIP network interface data structure belonging to this device.
* MAY be NULL as we do not support multiple devices yet.
*
*/
err_t cs8900if_init(struct netif *netif)
{
struct cs8900if *cs8900if;
cs8900if = mem_malloc(sizeof(struct cs8900if));
if (cs8900if == NULL)
{
LWIP_DEBUGF(NETIF_DEBUG, ("cs8900_input: out of memory for cs8900if\n"));
return ERR_MEM;
}
/* initialize lwip network interface ... */
#if LWIP_SNMP
/* ifType ethernetCsmacd(6) */
netif->link_type = 6;
netif->link_speed = 10000000;
netif->ts = 0;
netif->ifinoctets = 0;
netif->ifinucastpkts = 0;
netif->ifinnucastpkts = 0;
netif->ifindiscards = 0;
netif->ifoutoctets = 0;
netif->ifoutucastpkts = 0;
netif->ifoutnucastpkts = 0;
netif->ifoutdiscards = 0;
#endif
/* administrative details */
netif->name[0] = IFNAME0;
netif->name[1] = IFNAME1;
/* downward functions */
netif->output = etharp_output;
netif->linkoutput = cs8900_output;
/* initialize cs8900 specific interface state data pointer */
netif->state = cs8900if;
/* maximum transfer unit */
netif->mtu = 1500;
/* broadcast capability */
netif->flags = NETIF_FLAG_BROADCAST;
/* hardware address length */
netif->hwaddr_len = 6;
/* initially assume no ISQ event */
cs8900if->needs_service = 0;
/* set to 1 if polling method is used */
cs8900if->use_polling = 0;
#if (CS8900_STATS > 0)
/* number of interrupt service routine calls */
cs8900if->interrupts = 0;
cs8900if->missed = 0;
cs8900if->dropped = 0;
cs8900if->collisions = 0;
cs8900if->sentpackets = 0;
cs8900if->sentbytes = 0;
#endif
/* intialize the cs8900a chip */
return cs8900_init(netif);
}
#if 1
/**
* Dump an array of bytes inside a UDP message's data field.
*
* It is a self-contained function, independent of higher protocol layers or other
* functions, so it allows you to debug these higher layers, such as lwIP.
*
* @param p pointer to an array of bytes, at least with length 'len'
* @param len number of bytes available at the address pointed to by 'p'
*/
void cs8900_send_debug(u8_t *p, u16_t len)
{
s16_t tries = 0, i;
// network interface state
extern struct netif *ethif;
// exit if link has failed
PACKETPP = CS_PP_LINESTATUS;
if ((PPDATA & 0x0080U/*LinkOK*/) == 0) return; // TODO: find a correct error code
// transmit command
TXCMD = 0x00C9U;
// send at least 60 bytes
TXLENGTH = (14 + 20 + 8 + len < 60) ? 60 : (14 + 20 + 8 + len);
PACKETPP = CS_PP_BUSSTATUS;
// not ready for transmission and still within 100 retries?
while (((PPDATA & 0x0100U/*Rdy4TxNOW*/) == 0) && (tries++ < 100))
{
// throw away the last committed received frame
PACKETPP = CS_PP_RXCFG;
PPDATA = (0x0003U | 0x0040U/*Skip_1*/ | 0x0100U/*RxOKiE*/);
PACKETPP = CS_PP_BUSSTATUS;
/* cs8900if->dropped++; CHECK: we do not know if we actually will drop a frame here, do we? */
}
// ready to transmit?
if ((PPDATA & 0x0100U/*Rdy4TxNOW*/) != 0)
{
u16_t data, checksum = 0;
u32_t udp_checksum = 0;
// destination Ethernet address
RXTXREG = 0xa000U;
RXTXREG = 0xc524U;
RXTXREG = 0x6d72U;
// source Ethernet address
RXTXREG = htons(((u16_t)ethif->hwaddr[0] << 8U) | (u16_t)ethif->hwaddr[1]);
RXTXREG = htons(((u16_t)ethif->hwaddr[2] << 8U) | (u16_t)ethif->hwaddr[3]);
RXTXREG = htons(((u16_t)ethif->hwaddr[4] << 8U) | (u16_t)ethif->hwaddr[5]);
// frame type
RXTXREG = htons(0x0800);
// TOS, version
RXTXREG = htons(data = ((0x40 | 0x05) << 8) | 0x00);
checksum += data;
// length
RXTXREG = htons(data = 20 + 8 + len);
checksum += data;
// identifier
RXTXREG = htons(data = 0);
checksum += data;
// fragment offset
RXTXREG = htons(data = 0);
checksum += data;
// TTL, UDP protocol
RXTXREG = htons(data = (255U << 8) | 17U);
checksum += data;
checksum += (htonl(ethif->ip_addr.addr) & 0xffff0000U) >> 16;
checksum += (htonl(ethif->ip_addr.addr) & 0x0000ffffU);
checksum += 0xc0a8U;
checksum += 0x0001U;
checksum += 6; // LW: kludge/hack: checksum calculation seems to be wrong somehow
// LW: this seems (?) to fix it
// checksum
RXTXREG = htons(~checksum);
// source IP
RXTXREG = htons((htonl(ethif->ip_addr.addr) & 0xffff0000U) >> 16);
// source IP
RXTXREG = htons( htonl(ethif->ip_addr.addr) & 0x0000ffffU);
// destination IP
RXTXREG = htons(0xc0a8U);
// destination IP
RXTXREG = htons(0x0001U);
// source port 3000
RXTXREG = htons(3000U);
// destination port 3000
RXTXREG = htons(3000U);
// UDP length
RXTXREG = htons(len);
// UDP checksum (not present)
udp_checksum = (htonl(ethif->ip_addr.addr) & 0xffff0000U) >> 16;
udp_checksum += (htonl(ethif->ip_addr.addr) & 0x0000ffffU);
udp_checksum += 0xc0a8U;
udp_checksum += 0x0001U;
udp_checksum += 0x0011U;
udp_checksum += (8 + len);
udp_checksum += 3000;
udp_checksum += 3000;
udp_checksum += (8 + len);
udp_checksum += cs8900_chksum(p, len);
while (udp_checksum >> 16) {
udp_checksum = (udp_checksum & 0xffffUL) + (udp_checksum >> 16);
}
RXTXREG = htons(~(udp_checksum & 0xffff));
for (i = 0; i < len; i += 2)
{
RXTXREG = htons((p[i] << 8) | p[i + 1]);
}
// pad to 60 bytes
while (i < 60)
{
RXTXREG = 0;
i += 2;
}
}
}
static u32_t cs8900_chksum(void *dataptr, s16_t len)
{
u32_t acc = 0;
u16_t *ptr = (u16_t *)dataptr;
for(acc = 0; len > 1; len -= 2) {
acc += *ptr;
ptr++;
}
/* add up any odd byte */
if (len == 1) {
acc += htons((u16_t)((*(u8_t *)ptr) & 0xffU) << 8);
}
return acc;
}
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -