⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 if_8139.c

📁 开放源码实时操作系统源码.
💻 C
📖 第 1 页 / 共 4 页
字号:
     */
    cyg_drv_interrupt_create(rltk8139_info->vector,
                             rltk8139_info->isr_priority,
                             (CYG_ADDRWORD)sc,
                             rltk8139_isr,
                             eth_drv_dsr,
                             &rltk8139_info->interrupt_handle,
                             &rltk8139_info->interrupt);
    cyg_drv_interrupt_attach(rltk8139_info->interrupt_handle);
#endif
  }
  else {
#ifdef DEBUG_RLTK8139_DRIVER
    diag_printf(" Does not generate interrupts.\n");
#endif
  }

  /*
   * Call 'cyg_pci_configure_device' for those platforms that do not
   * configure the PCI bus during HAL initialization. According to Nick
   * Garnett, there are good reasons to not configure PCI devices during HAL
   * initialization. Also according to Nick, calling
   * 'cyg_pci_configure_device' for devices already configured should have no
   * effect and thus is safe to do.
   */
  if (cyg_pci_configure_device(&pci_device_info)) {
#ifdef DEBUG_RLTK8139_DRIVER
    int i;
    diag_printf("Found device #%d on bus %d, devfn 0x%02x:\n", n_th,
                CYG_PCI_DEV_GET_BUS(pci_device_id),
                CYG_PCI_DEV_GET_DEVFN(pci_device_id));

    if (pci_device_info.command & CYG_PCI_CFG_COMMAND_ACTIVE)
      diag_printf(" Note that board is active. Probed"
                  " sizes and CPU addresses are invalid!\n");

    diag_printf(" Vendor    0x%04x", pci_device_info.vendor);
    diag_printf("\n Device    0x%04x", pci_device_info.device);
    diag_printf("\n Command   0x%04x, Status 0x%04x\n",
                pci_device_info.command, pci_device_info.status)
      ;

    diag_printf(" Class/Rev 0x%08x", pci_device_info.class_rev);
    diag_printf("\n Header 0x%02x\n", pci_device_info.header_type);

    diag_printf(" SubVendor 0x%04x, Sub ID 0x%04x\n",
                pci_device_info.header.normal.sub_vendor,
                pci_device_info.header.normal.sub_id);

    for(i = 0; i < CYG_PCI_MAX_BAR; i++) {
      diag_printf(" BAR[%d]    0x%08x /", i, pci_device_info.base_address[i]);
      diag_printf(" probed size 0x%08x / CPU addr 0x%08x\n",
                  pci_device_info.base_size[i], pci_device_info.base_map[i]);
    }
#endif
  }
  else {
#ifdef DEBUG_RLTK8139_DRIVER
    diag_printf("Failed to configure 8139 device #%d\n", n_th);
#endif
    return false;
  }

  /*
   * Enable memory mapped and port based I/O and busmastering. We currently
   * only support IO space accesses; memory mapping is enabled so that bit
   * DVRLOAD in CONFIG1 is cleared automatically.
   *
   * NOTE: it seems that for some configurations/HALs, the device is already
   *       activated at this point, even though eCos' documentation suggests
   *       it shouldn't be. At least in my case, this is not a problem.
   */
  cyg_pci_read_config_uint16(pci_device_info.devid, CYG_PCI_CFG_COMMAND,
                             &command);
  command |= (CYG_PCI_CFG_COMMAND_IO | CYG_PCI_CFG_COMMAND_MEMORY
              | CYG_PCI_CFG_COMMAND_MASTER);
  cyg_pci_write_config_uint16(pci_device_info.devid, CYG_PCI_CFG_COMMAND,
                              command);

  /*
   * This is the base address used to talk to the device. The 8139's IOAR
   * and MEMAR registers are BAR0 and BAR1, respectively.
   */
  rltk8139_info->base_address = pci_device_info.base_map[0];

  /*
   * Read the MAC address. The RealTek data sheet seems to claim this should
   * only be read in 4 byte accesses, but the code below works just fine.
   */
  for (found = 0; found < 6; ++found)
    rltk8139_info->mac[found] = INB(rltk8139_info->base_address + IDR0 + found);

  /*
   * This is the indicator for "uses an interrupt". The code was lifted
   * from the eCos Intel 82559 driver.
   */
  if (rltk8139_info->interrupt_handle != 0) {
    cyg_drv_interrupt_acknowledge(rltk8139_info->vector);

#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
    cyg_drv_interrupt_unmask(rltk8139_info->vector);
#endif
  }

  return true;
}

#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
/*
 * Interrupt service routine. We do not clear the interrupt status bits
 * (since this should really only be done after handling whatever caused
 * the interrupt, and that is done in the '_deliver' routine), but instead
 * clear the interrupt mask.
 *
 * If we are sharing interrupts with other devices, we have two options
 * (configurable):
 *
 * 1. Mask the interrupt vector completly. Personally I think this is a bad
 *    idea because the other devices sharing this interrupt are also masked
 *    until the network thread gets around to calling the '_deliver' routine.
 *
 * 2. Use the interrupt mask register in the 8139 to mask just the interrupt
 *    request coming from the 8139. This way, the other devices' requests
 *    can still be serviced.
 */
static cyg_uint32
rltk8139_isr(cyg_vector_t vector, cyg_addrword_t data)
{
  Rltk8139_t *rltk8139_info;
#ifdef CYGPKG_DEVS_ETH_RLTK_8139_SHARE_INTERRUPTS
  cyg_uint16 isr;
#endif

  rltk8139_info = (Rltk8139_t *)(((struct eth_drv_sc *)data)->driver_private);

#ifdef CYGPKG_DEVS_ETH_RLTK_8139_SHARE_INTERRUPTS
  /*
   * If interrupt sharing is enabled, check if the interrupt is really
   * intended for us. Note that while the RealTek data sheet claims that
   * reading the interrupt status register will clear all it's bits,
   * this is not true, so we can read it without problems.
   */
  if (!(isr = INW(rltk8139_info->base_address + ISR)))
    return 0;
#endif

#ifdef CYGPKG_DEVS_ETH_RLTK_8139_MASK_INTERRUPTS_IN_8139
  /* Clear the interrupt mask to stop the current request. */
  OUTW(0, rltk8139_info->base_address + IMR);
#else
  /* Mask the interrupt */
  cyg_interrupt_mask(vector);
#endif

  /* Acknowledge the interrupt for those platforms were this is necessary */
  cyg_interrupt_acknowledge(vector);

  return (CYG_ISR_HANDLED | CYG_ISR_CALL_DSR);
}
#endif /* ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE */

/*
 * Reset the chip. This function is not exported to higher level software.
 */
static void
rltk8139_reset(struct eth_drv_sc *sc)
{
  rltk8139_stop(sc);
  rltk8139_start(sc, NULL, 0);
}

#ifdef ETH_DRV_SET_MC_LIST
/*
 * I assume (hope !) that this is identical to Ethernet's CRC algorithm
 * specified in IEEE 802.3. It does seem to calculate the same CRC that
 * the 8139 itself does, so I think it is correct.
 * Note that while Ethernet's polynomial is usually specified as 04C11DB7,
 * we must use EDB88320 because we shift the bits to the left, not the right.
 * (See ftp://ftp.rocksoft.com/papers/crc_v3.txt for a good description of
 * CRC algorithms).
 */
static cyg_uint32
ether_crc(cyg_uint8 *data, int length)
{
  int bit;
  cyg_uint32 crc = 0xFFFFFFFFU;

  while (length-- > 0) {
    crc ^= *data++;
    for (bit = 0; bit < 8; ++bit) {
      if (crc & 1)
        crc = (crc >> 1) ^ 0xEDB88320U;
      else
        crc = (crc >> 1);
    }
  }

  return crc ^ 0xFFFFFFFFU;
}


/*
 * Set up multicast filtering. The way I understand existing driver code
 * (Linux and OpenBSD), the 8139 calculates the ethernet CRC of
 * incoming addresses and uses the top 6 bits as an index into a hash
 * table. If the corresponding bit is set in MAR0..7, the address is
 * accepted.
 */
static void
rltk8139_set_multicast_list(Rltk8139_t *rltk8139_info,
                            struct eth_drv_mc_list *mc_list)
{
  cyg_uint32 mar[2], hash;
  int i;

  /* If 'mc_list' is NULL, accept all multicast packets. */
  if (!mc_list) {
    mar[0] = 0xFFFFFFFFU;
    mar[1] = 0xFFFFFFFFU;
  }
  else {
    mar[0] = 0;
    mar[1] = 0;

    for (i = 0; i < mc_list->len; ++i) {
      hash = ether_crc(&mc_list->addrs[i][0], ETHER_ADDR_LEN) >> 26;
      mar[hash >> 5] |= (1 << (hash & 0x1F));
    }
  }

  /* Program the new filter values */
  OUTL(mar[0], rltk8139_info->base_address + MAR0);
  OUTL(mar[1], rltk8139_info->base_address + MAR4);
}
#endif /* ifdef ETH_DRV_SET_MC_LIST */


/*
 * Initialize the network interface. Since the chips is reset by calling
 * _stop() and _start(), any code that will never need to be executed more
 * than once after system startup should go here.
 */
static bool
rltk8139_init(struct cyg_netdevtab_entry *tab)
{
  struct eth_drv_sc *sc;
  Rltk8139_t *rltk8139_info;


  sc = (struct eth_drv_sc *)(tab->device_instance);
  rltk8139_info = (Rltk8139_t *)(sc->driver_private);

  /*
   * Initialize the eCos PCI library. According to the documentation, it
   * is safe to call this function multiple times, so we call it just to
   * be sure it has been done.
   */
  cyg_pci_init();

  /*
   * Scan the PCI bus for the specified chip. The '_find' function will also
   * do some basic PCI initialization.
   */
  if (!rltk8139_find(rltk8139_info->device_num, sc)) {
#ifdef DEBUG_RLTK8139_DRIVER
    diag_printf("rltk8139_init: could not find RealTek 8139 chip #%d.\n",
                rltk8139_info->device_num);
#endif
    return false;
  }

  /* platform depends initialize */
  CYGHWR_RLTK_8139_PLF_INIT(sc);

  /*
   * The initial tx threshold is set here to prevent it from being reset
   * with every _start().
   */
  rltk8139_info->tx_threshold = 3;

  /* Initialize upper level driver */
  (sc->funs->eth_drv->init)(sc, rltk8139_info->mac);

  return true;
}


/*
 * (Re)Start the chip, initializing data structures and enabling the
 * transmitter and receiver. Currently, 'flags' is unused by eCos.
 */
static void
rltk8139_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
{
  Rltk8139_t *rltk8139_info;
  int i;


#ifdef DEBUG_RLTK8139_DRIVER
  diag_printf("rltk8139_start(%s)\n", sc->dev_name);
#endif

  rltk8139_info = (Rltk8139_t *)(sc->driver_private);

  /*
   * Reset the chip. Existing driver code implies that this may take up
   * to 10ms; since I don't know under what exact circumstances this code may
   * be called I busy wait here regardless.
   */
  OUTB(RST, rltk8139_info->base_address + CR);
  while (INB(rltk8139_info->base_address + CR) & RST);

#ifdef DEBUG_RLTK8139_DRIVER
  diag_printf("rltk8139_start(%s): 8139 was successfully reset.\n",
              sc->dev_name);
#endif

  /*
   * Clear the key storage area. These keys are used by the eCos networking
   * support code to keep track of individual packets.
   */
  for (i = 0; i < NUM_TX_DESC; ++i)
    rltk8139_info->tx_desc_key[i] = 0;

  /* Initialize transmission buffer control */
  rltk8139_info->tx_free_desc = 0;
  rltk8139_info->tx_num_free_desc = NUM_TX_DESC;

  /*
   * Set the requested MAC address if enaddr != NULL. This is a special
   * feature of my '_start' function since it's used to reset the chip after
   * errors as well.
   */
  if (enaddr != NULL) {
    for (i = 0; i < 6; ++i) {
      rltk8139_info->mac[i] = enaddr[i];
      OUTB(enaddr[i], rltk8139_info->base_address + IDR0 + i);
    }
  }

  /*
   * Now setup the transmission and reception buffers. These could be done
   * in _init() and kept around, but putting them here fits better logically.
   */
  OUTL(CYGARC_PCI_DMA_ADDRESS((cyg_uint32)(rltk8139_info->tx_buffer
                                            + 0 * TX_BUF_SIZE)),
       rltk8139_info->base_address + TSAD0);
  OUTL(CYGARC_PCI_DMA_ADDRESS((cyg_uint32)(rltk8139_info->tx_buffer
                                            + 1 * TX_BUF_SIZE)),
       rltk8139_info->base_address + TSAD1);
  OUTL(CYGARC_PCI_DMA_ADDRESS((cyg_uint32)(rltk8139_info->tx_buffer
                                            + 2 * TX_BUF_SIZE)),
       rltk8139_info->base_address + TSAD2);
  OUTL(CYGARC_PCI_DMA_ADDRESS((cyg_uint32)(rltk8139_info->tx_buffer
                                            + 3 * TX_BUF_SIZE)),
       rltk8139_info->base_address + TSAD3);
  OUTL(CYGARC_PCI_DMA_ADDRESS((cyg_uint32)rltk8139_info->rx_ring),
       rltk8139_info->base_address + RBSTART);

  /*
   * Enable the transmitter and receiver, then clear the missed packet
   * counter.
   */
  OUTB(INB(rltk8139_info->base_address + CR) | (TE|RE),
       rltk8139_info->base_address + CR);
  OUTL(0, rltk8139_info->base_address + MPC);

  /*
   * It seems the receiver and transmitter configuration can only
   * be set after the transmitter/receiver have been enabled.
   */
  OUTL(TXCFG, rltk8139_info->base_address + TCR);
  OUTL(RXCFG | AM, rltk8139_info->base_address + RCR);

  /*
   * Enable the transmitter and receiver again. I'm not sure why this is
   * necessary; the Linux driver does it so we do it here just to be on
   * the safe side.
   */
  OUTB(INB(rltk8139_info->base_address + CR) | (TE|RE),
       rltk8139_info->base_address + CR);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -