cs8900if.c

来自「Keil下移植好的lwip基于c166」· C语言 代码 · 共 842 行 · 第 1/2 页

C
842
字号
        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)
{
  // amount of ISQ's to handle (> 0) in one cs8900_service() call
  unsigned char events2service = 1;
  // 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*/)
    {
  	  ((struct cs8900if *)netif->state)->missed += (irq_status >> 6);
  	}
    else if ((irq_status & 0x003fU) == 0x0012U/*TxCOL Event*/)
    {
  	  ((struct cs8900if *)netif->state)->collisions += (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)
  // read RxMiss Counter (zeroes itself upon read)
  PACKETPP = CS_PP_RXMISS;
  ((struct cs8900if *)netif->state)->missed += (PPDATA >> 6);
  // read RxCol Counter (zeroes itself upon read)
  PACKETPP = CS_PP_TXCOL;
  ((struct cs8900if *)netif->state)->collisions += (PPDATA >> 6);
#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);
  }
}

/**
 * Writing an IP packet (to be transmitted) to the CS8900.
 *
 * Before writing a frame to the CS8900, the ARP module is asked to resolve the
 * Ethernet MAC address. The ARP module might undertake actions to resolve the
 * address first, and queue this packet for later transmission.
 *
 * @param netif The lwIP network interface data structure belonging to this device.
 * @param p pbuf to be transmitted (or the first pbuf of a chained list of pbufs).
 * @param ipaddr destination IP address.
 *
 * @internal It uses the function cs8900_input() that should handle the actual
 * reception of bytes from the network interface.
 *
 */
err_t cs8900if_output(struct netif *netif, struct pbuf *p, struct ip_addr *ipaddr)
{
  struct cs8900if *cs8900if = netif->state;
  p = etharp_output(netif, ipaddr, p);
  /* network hardware address obtained? */
  if (p != NULL)
  {
	  /* send out the packet */
    cs8900_output(netif, p);
    p = NULL;
  }
  // { p == NULL }
	else
	{
	  /* we cannot tell if the packet was sent, the packet could have been queued */
    /* on an ARP entry that was already pending. */
	}
  return ERR_OK;
}
/**
 * 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 cs8900if *cs8900if = netif->state;
  struct eth_hdr *ethhdr = NULL;
  struct pbuf *p = NULL, *q = 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;
  
  q = NULL;
  switch(htons(ethhdr->type)) {
  /* IP packet? */
  case ETHTYPE_IP:
    /* update ARP table, obtain first queued packet */
    q = etharp_ip_input(netif, p);
    /* skip Ethernet header */
    pbuf_header(p, -14);
    /* pass to network layer */
    netif->input(p, netif);
    break;
  /* ARP packet? */
  case ETHTYPE_ARP:
    /* pass p to ARP module, get ARP reply or ARP queued packet */
    q = etharp_arp_input(netif, (struct eth_addr *)&netif->hwaddr, p);
    break;
  /* unsupported Ethernet packet type */
  default:
    /* free pbuf */
    pbuf_free(p);
    p = NULL;
    break;
  }
  /* send out the ARP reply or ARP queued packet */
  if (q != NULL) {
    /* q pbuf has been succesfully sent? */
    if (cs8900_output(netif, q) == ERR_OK)
    {
      pbuf_free(q);
      q = NULL;
    }
    else
    {
      /* TODO: re-queue packet in the ARP cache here (?) */
      pbuf_free(q);
      q = NULL;
    }
  }
}
/**
 * Initialize the CS8900 Ethernet MAC/PHY device driver.
 *
 * @param netif The lwIP network interface data structure belonging to this device.
 *
 */
void cs8900if_init(struct netif *netif)
{
  struct cs8900if *cs8900if;

  cs8900if = mem_malloc(sizeof(struct cs8900if));
	if(cs8900if == NULL) return;

  // initialize lwip network interface
  netif->name[0] = IFNAME0;
  netif->name[1] = IFNAME1;
  netif->output = cs8900if_output;
  netif->linkoutput = cs8900_output;

  // initialize cs8900 specific interface structure
  netif->state = cs8900if;

#if 0
  /* maximum transfer unit */
  netif->mtu = 1500;
  
  /* broadcast capability */
  netif->flags = NETIF_FLAG_BROADCAST;
  
  /* hardware address length */
  netif->hwaddr_len = 6;
#endif
  // 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->sentpackets = 0;
  cs8900if->sentbytes = 0;
#endif

  // intialize the cs8900a chip
  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(unsigned char *p, unsigned int len)
{
	int 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));
	  // UDP data
    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, int 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 + =
减小字号Ctrl + -
显示快捷键?