POK
/home/jaouen/pok_official/pok/trunk/libpok/drivers/rtl8029.c File Reference

RTL8029 driver. More...

Go to the source code of this file.

Functions

void rtl8029_read (pok_port_id_t port_id, void *data, uint32_t len)
 Reads data from the corresponding network stack.
void rtl8029_write (pok_port_id_t port_id, const void *data, uint32_t len)
 Send data to the interface.
void rtl8029_polling ()
 Polls rtl8029 device.
void rtl8029_init ()
 Initializes rtl8029 device.

Detailed Description

RTL8029 driver.

Author:
Laurent
Date:
PFE GISTR 2010

Definition in file rtl8029.c.


Function Documentation

void rtl8029_init ( )

Initializes rtl8029 device.

Seeks and registers PCI interface, set configuration and fills the dev structure.

Definition at line 382 of file rtl8029.c.

{
  dev.pci.vendorid = 0x10ec;
  dev.pci.deviceid = 0x8029;
  dev.pci.io_range = 0x10;

  if (pci_register(&(dev.pci)) != 0)
  {
    printf("rtl8029: PCI init failed!\n");
    return;
  }

  dev.addr = dev.pci.bar[0] & (~0x1F);

  unsigned char i = 0;
  unsigned char buf[6 * 2]; // used for MAC address

  NE2000_SELECT_PAGE(&dev, 0);

  /* This bit is the STOP command. When it is set, no packets will be
     received or transmitted. POWER UP=1. */
  outb(NE2000_CR_STP, dev.addr + NE2000_CR);

  // Sets several options... Read the datasheet!
  outb(0x00, dev.addr + NE2000_TCR);
  outb(NE2000_RCR_AB, dev.addr + NE2000_RCR);
  outb(NE2000_DCR_LS | NE2000_DCR_FT1, dev.addr + NE2000_DCR);

  /* The Page Start register sets the start page address
     of the receive buffer ring. */
  outb(NE2000_RXBUF, dev.addr + NE2000_PSTART);
  /* The Page Stop register sets the stop page address
     of the receive buffer ring. */
  outb(NE2000_MEMSZ, dev.addr + NE2000_PSTOP);
  /* This register is used to prevent overwrite of the receive buffer ring.
     It is typically used as a pointer indicating the last receive buffer
     page the host has read. */
  outb(NE2000_RXBUF, dev.addr + NE2000_BNRY);

  /* These two registers set the data byte counts of remote DMA. */
  outb(0, dev.addr + NE2000_RBCR0);
  outb(0, dev.addr + NE2000_RBCR1);

  NE2000_SELECT_PAGE(&dev, 1);

  /* This register points to the page address of the first receive buffer
     page to be used for a packet reception. */
  outb(NE2000_RXBUF + 1, dev.addr + NE2000_CURR);

  // Init mac address
  /* Here's something I do not understand... Section 6.2.2 of the datasheet
     says bytes 00H-05H of the PROM corresponds to the Ethernet ID. But it
     looks like each byte of the MAC address is written twice...
     Therefore I read 2 * sizeof(mac) and select one of the two bytes
     corresponding to the MAC... Weird... Really... */
  ne2000_read(&dev, buf, 6 * 2, 0);
  for (i = 0; i < 6; i++)
    dev.mac[i] = buf[i * 2];

  /* These registers contain my Ethernet node address and are used to compare
    the destination address of incoming packets for acceptation or rejection.*/
  outb(dev.mac[0], dev.addr + NE2000_PAR0);
  outb(dev.mac[1], dev.addr + NE2000_PAR1);
  outb(dev.mac[2], dev.addr + NE2000_PAR2);
  outb(dev.mac[3], dev.addr + NE2000_PAR3);
  outb(dev.mac[4], dev.addr + NE2000_PAR4);
  outb(dev.mac[5], dev.addr + NE2000_PAR5);

  NE2000_SELECT_PAGE(&dev, 0);

  // Start command
  outb(NE2000_CR_STA, dev.addr + NE2000_CR);

  // Reactivating interrupts
  /* ISR register must be cleared after power up. */
  outb(0xFF, dev.addr + NE2000_ISR);
  /* All bits correspond to the bits in the ISR register. POWER UP=all 0s.
     Setting individual bits will enable the corresponding interrupts. */
  /* Since POK use polling, ALL interrupts are disabled */
  outb(0x00, dev.addr + NE2000_IMR);

  for (i = 0; i < 20; i++) /* TODO: random constant */
  {
    dev.recv_buf[i].len = 0;
    dev.recv_buf[i].off = 0;
  }

  return;
}
void rtl8029_polling ( )

Polls rtl8029 device.

Watches for events, typically for receiving queued packets.

Definition at line 279 of file rtl8029.c.

{
  unsigned char state; // ISR state

  NE2000_SELECT_PAGE(&dev, 0);

  while (1)
  {
    // do we have an interrupt flag set?
    if ((state = pok_inb(dev.addr + NE2000_ISR)) == 0)
      continue;

    if (state & NE2000_ISR_PRX)
    {
      if ((pok_inb(dev.addr + NE2000_RSR) & NE2000_RSR_PRX) == 0)
      {
        // error
      }

      printf("[*]\n");

      /* no errors */
      s_ne2000_header   ne2000_hdr;     // ne2000 packet header
      unsigned short    offset;         // dma offset
      unsigned char     start, end;     // pointers for the ring buffer
      pok_packet_t      recv_packet;

      while (1)
      {

        /* This register is used to prevent overwrite of the receive buffer ring.
           It is typically used as a pointer indicating the last receive buffer
           page the host has read.*/
        start = pok_inb(dev.addr + NE2000_BNRY) + 1;

        /* This register points to the page address of the first receive
           buffer page to be used for a packet reception. */
        NE2000_SELECT_PAGE(&dev, 1);
        end = pok_inb(dev.addr + NE2000_CURR);
        NE2000_SELECT_PAGE(&dev, 0);

        if ((end % NE2000_MEMSZ) == (start % NE2000_MEMSZ) + 1)
        {
          break;
        }

        /* et on decapsule! */
        offset = start << 8;
        // ne2000 header
        offset += ne2000_read(&dev, &ne2000_hdr, sizeof(s_ne2000_header),
                              offset);

        ne2000_read(&dev, &recv_packet,
                    ne2000_hdr.size - sizeof(s_ne2000_header), offset);
        rtl8029_enqueue(&recv_packet);

        // update the BNRY register... almost forgot that
        outb(ne2000_hdr.next > NE2000_MEMSZ ?
             NE2000_RXBUF - 1 : ne2000_hdr.next - 1, dev.addr + NE2000_BNRY);

      }

      outb(NE2000_ISR_PRX, dev.addr + NE2000_ISR); // Clear PRX flag
    }

    if (state & NE2000_ISR_PTX)
    {
      outb(NE2000_ISR_PTX, dev.addr + NE2000_ISR); // Clear PTX flag
    }

    if (state & NE2000_ISR_RXE)
    {
      outb(NE2000_ISR_RXE, dev.addr + NE2000_ISR); // Clear RXE flag
    }

    if (state & NE2000_ISR_TXE)
    {
      outb(NE2000_ISR_TXE, dev.addr + NE2000_ISR); // Clear TXE flag
    }

    if (state & NE2000_ISR_OVW)
    {
      outb(NE2000_ISR_OVW, dev.addr + NE2000_ISR); // Clear OVW flag
    }

    if (state & NE2000_ISR_CNT)
    {
      outb(NE2000_ISR_CNT, dev.addr + NE2000_ISR); // Clear CNT flag
    }

    if (state & NE2000_ISR_RST)
    {
      outb(NE2000_ISR_RST, dev.addr + NE2000_ISR); // Clear RST bit
    }
  }
}
void rtl8029_read ( pok_port_id_t  port_id,
void *  data,
uint32_t  len 
)

Reads data from the corresponding network stack.

Reads enqueued data in the stack partition.

Definition at line 146 of file rtl8029.c.

{
  pok_port_id_t global;
  pok_ret_t     ret;

  ret = pok_port_virtual_get_global (port_id, &global);

  if (ret == POK_ERRNO_OK)
  {
    char        *dest = data;
    pok_queue_t* queue = dev.recv_buf + global;
    uint32_t    size = len < queue->len ? len : queue->len;
    uint32_t    copied = 0;

    printf ("[RTL8029] READ DATA FROM LOCAL PORT %d "
            "GLOBAL_PORT=%d), size=%d\n", port_id, global, len);

    /* is there something to read ? */
    if (queue->len == 0)
    {
      printf("rtl8029_read: error: empty read ring buffer %d!\n", port_id);
      return;
    }

    /* copy from the queue to the buffer */
    for (copied = 0; copied < size; copied++)
    {
      dest[copied % RECV_BUF_SZ] = queue->data[queue->off];
      queue->off = (queue->off + 1) % RECV_BUF_SZ;
    }

    /* updating data length in this queue */
    queue->len -= size;
  }
}
void rtl8029_write ( pok_port_id_t  port_id,
const void *  data,
uint32_t  len 
)

Send data to the interface.

Writes data to be sent to network.

Definition at line 187 of file rtl8029.c.

{
  uint32_t        nbdest;
  uint32_t        tmp;
  uint32_t        dest;
  pok_ret_t       ret;
  char            node2[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
  pok_packet_t    packet;
  const char*     d;
  size_t          cpylen = 0;
  size_t          sndlen = 0;
  unsigned char state; // ISR state

  ret = pok_port_virtual_nb_destinations (port_id, &nbdest);
  if (ret != POK_ERRNO_OK)
  {
    return;
  }

  for (tmp = 0 ; tmp < nbdest ; tmp++)
  {
    ret = pok_port_virtual_destination (port_id, tmp, &dest);
    if (ret == POK_ERRNO_OK)
    {
      printf ("[RTL8029] SEND DATA THROUGH NETWORK FROM LOCAL PORT %d "
              "TO GLOBAL PORT %d, size=%d\n", port_id, dest, len);

      memcpy(packet.eth.src, dev.mac, ETH_MAC_LEN);
      memcpy(packet.eth.dst, node2, ETH_MAC_LEN);
      packet.eth.ethertype = 0x4242;
      packet.udp.src = port_id;
      packet.udp.dst = dest;

      for (d = data; len != 0; len -= cpylen, data += cpylen)
      {
        // too short; let's cut
        if (len <= NET_DATA_MINLEN)
        {
          cpylen = len;
          sndlen = ETH_DATA_MINLEN + sizeof(eth_hdr_t);
        }
        else
        {
          // too big; let's pad
          if (len >= NET_DATA_MAXLEN)
          {
            cpylen = NET_DATA_MAXLEN;
            sndlen = ETH_DATA_MAXLEN + sizeof(eth_hdr_t);
          }
          // normal
          else
          {
            cpylen = len;
            sndlen = sizeof(eth_hdr_t) + sizeof(udp_hdr_t) + cpylen;
          }
        }

        packet.udp.len = cpylen;
        memcpy(&(packet.data), data, cpylen);

        ne2000_write(&dev, &packet, sndlen, NE2000_TXBUF * 256);

        do
        {
          state = pok_inb(dev.addr + NE2000_ISR);
        }
        while ((state & NE2000_ISR_RDC) != NE2000_ISR_RDC);

        /* This register sets the start page address of
           the packet to the transmitted. */
        outb(NE2000_TXBUF, dev.addr + NE2000_TPSR); //?

        /* These two registers set the byte counts of
           the packet to be transmitted. */
        outb(sndlen, dev.addr + NE2000_TBCR0);
        outb(sndlen >> 8, dev.addr + NE2000_TBCR1);

        /* This bit must be set to transmit a packet. */
        outb(pok_inb(dev.addr + NE2000_CR) | NE2000_CR_TXP,
             dev.addr + NE2000_CR);

        outb(NE2000_ISR_RDC, dev.addr + NE2000_ISR); // Clear RDC bit
      }
    }
  }
}