Beispiel #1
0
void irq_handler (unsigned int port_number)
{
  system_call_thread_name_set ("IRQ handler");

  if (system_call_irq_register (serial_port[port_number].irq, "Serial (UART)")
      != STORM_RETURN_SUCCESS)
  {
    log_print_formatted (&log_structure, LOG_URGENCY_EMERGENCY,
               "Could not allocate IRQ %d.", serial_port[port_number].irq);
    return;
  }

  while (TRUE)
  {
    system_call_irq_wait (serial_port[port_number].irq);
    handle_irq (port_number);
    system_call_irq_acknowledge (serial_port[port_number].irq);
  }
}
Beispiel #2
0
static void handle_8139 (pci_device_info_type *device_info)
{
  unsigned int counter;
  u16 port_base = MAX_U16;
  u16 ports = 0;
  realtek_device_type *device;
  unsigned int physical, physical_index;
  ipc_structure_type ipc_structure;

  system_thread_name_set ("Initialising");
  
  for (counter = 0; counter < PCI_NUMBER_OF_RESOURCES; counter++)
  {
    if ((device_info->resource[counter].flags & PCI_RESOURCE_IO) != 0)
    {
      port_base = device_info->resource[counter].start;
      ports = (device_info->resource[counter].end -
               device_info->resource[counter].start + 1);
    }
  }

  if (port_base == MAX_U16)
  {
    log_print_formatted (&log_structure, LOG_URGENCY_ERROR, 
                         "No port range found -- hardware possibly broken or incompatible?");
    return;
  }

  system_call_port_range_register (port_base, ports, "Realtek 8139");

  memory_allocate ((void **) &device, sizeof (realtek_device_type));

  device->port_base = port_base;
  device->irq = device_info->irq;

  /* Initialise the adapter. */

  system_port_out_u8 (port_base + Config1, 0x00);
  
  if (read_eeprom (port_base, 0) != 0xFFFF) 
  {
    for (counter = 0; counter < 3; counter++) 
    {
      ((u16 *)(device->ethernet_address))[counter] =
        system_little_endian_to_native_u16 (read_eeprom (port_base, 
                                                        counter + 7));
    }
  }
  else 
  {
    for (counter = 0; counter < 6; counter++)
    {
      device->ethernet_address[counter] = 
        system_port_in_u8 (port_base + MAC0 + counter);
    }
  }

  log_print_formatted 
    (&log_structure, LOG_URGENCY_INFORMATIVE,
     "Realtek 8139 at 0x%X, IRQ %d, ethernet address: %02X:%02X:%02X:%02X:%02X:%02X",
     port_base, device_info->irq, device->ethernet_address[0], 
     device->ethernet_address[1], device->ethernet_address[2], 
     device->ethernet_address[3], device->ethernet_address[4],
     device->ethernet_address[5]);

  /* Find the connected MII transceivers. */

  for (physical = 0, physical_index = 0; physical < 32 &&
         physical_index < sizeof (device->mii_address); physical++)
  {
    int mii_status = mdio_read (port_base, physical, 1);

    if (mii_status != 0xFFFF && mii_status != 0x0000) 
    {
      device->mii_address[physical_index] = physical;
      physical_index++;

      log_print_formatted (&log_structure, LOG_URGENCY_INFORMATIVE,
                           "MII transceiver found at address %d.",
                           physical);
    }
  }

  if (physical_index == 0) 
  {
    if (realtek_debug > 1)
    {
      log_print_formatted (&log_structure, LOG_URGENCY_INFORMATIVE,
                           "No MII transceivers found! Assuming SYM "
                           "transceiver.");
    }

    device->mii_address[0] = -1;
  }

  /* Soft reset the chip. */

  system_port_out_u8 (port_base + ChipCommand, CommandReset);

  pci_allocate_buffer ((void **) &device->tx_buffers_dma,
                       (void **) &device->tx_buffers,
                       TX_BUFFER_SIZE * NUMBER_OF_TX_DESCRIPTORS);
  pci_allocate_buffer ((void **) &device->rx_ring_dma,
                       (void **) &device->rx_ring,
                       RX_BUFFER_LENGTH + 16);

  device->tx_full = FALSE;
  device->current_rx = 0;
  device->dirty_tx = device->current_tx = 0;

  for (counter = 0; counter < NUMBER_OF_TX_DESCRIPTORS; counter++) 
  {
    device->tx_buffer[counter] = &device->tx_buffers[counter * TX_BUFFER_SIZE];
  }
  
  /* Check that the chip has finished the reset. */

  for (counter = 0; counter < 1000; counter++)
  {
    if ((system_port_in_u8 (port_base + ChipCommand) & CommandReset) == 0)
    {
      break;
    }
  }

  /* Must enable Tx/Rx before setting transfer thresholds! */

  system_port_out_u8 (port_base + ChipCommand, 
                      CommandRxEnable | CommandTxEnable);
  system_port_out_u32 (port_base + RxConfig, 
                       (RX_FIFO_THRESHOLD << 13) | 
                       (RX_BUFFER_LENGTH_INDEX << 11) | 
                       (RX_DMA_BURST << 8));

  system_port_out_u32 (port_base + TxConfig, (TX_DMA_BURST << 8) | 0x03000000);

  if (device->mii_address[0] >= 0) 
  {
    u16 mii_reg5 = mdio_read (port_base, device->mii_address[0], 5);

    if (mii_reg5 == 0xffff)
    {
    }
    else if ((mii_reg5 & 0x0100) == 0x0100 || (mii_reg5 & 0x00C0) == 0x0040)
    {
      device->full_duplex = TRUE;
    }

    log_print_formatted (&log_structure, LOG_URGENCY_INFORMATIVE,
                         "Setting %s%s-duplex based on"
                         " auto-negotiated partner ability %4.4x.\n",
                         mii_reg5 == 0 ? "" :
                         ((mii_reg5 & 0x0180) != 0) ? "100 Mbps " : 
                         "10 Mbps ",
                         device->full_duplex ? "full" : "half", mii_reg5);
  }
  
  system_port_out_u8 (port_base + Config9346, 0xC0);
  system_port_out_u8 (port_base + Config1, device->full_duplex ? 0x60 : 0x20);
  system_port_out_u8 (port_base + Config9346, 0x00);

  system_port_out_u32 (port_base + RxBuffer, (u32) device->rx_ring_dma);
  
  /* Start the chip's Tx and Rx process. */
  
  system_port_out_u32 (port_base + RxMissed, 0);
  set_rx_mode (device);
  
  system_port_out_u8 (port_base + ChipCommand,
                      CommandRxEnable | CommandTxEnable);
  
  /* Enable all known interrupts by setting the interrupt mask. */
  
  system_port_out_u16 (port_base + InterruptMask,
                       PCIError | PCSTimeout | RxUnderrun | RxOverflow | 
                       RxFIFOOverrun | TxError | TxOK | RxError | RxOK);


  if (system_thread_create () == SYSTEM_RETURN_THREAD_NEW)
  {
    system_thread_name_set ("IRQ handler");

    if (system_call_irq_register (device_info->irq, "Realtek 8139") != 
        SYSTEM_RETURN_SUCCESS)
    {
      log_print_formatted (&log_structure, LOG_URGENCY_EMERGENCY, 
                           "Couldn't set up IRQ handler");
      return;
    }

    /* Loop and handle interrupts. */
    
    while (TRUE)
    {
      int bogus_count = max_interrupt_work;
      unsigned status, link_changed = 0;
      
      system_call_irq_wait (device->irq);
      
      do 
      {
        status = system_port_in_u16 (port_base + InterruptStatus);
        
        /* Acknowledge all of the current interrupt sources ASAP, but
           an first get an additional status bit from CSCR. */
        
        if ((status & RxUnderrun) && 
            system_port_in_u16 (port_base + CSCR) & CSCR_LinkChangeBit)
        {
          link_changed = 1;
        }
        
        system_port_out_u16 (port_base + InterruptStatus, status);
        
        if ((status & (PCIError | PCSTimeout | RxUnderrun | RxOverflow | 
                       RxFIFOOverrun | TxError | TxOK | RxError | RxOK)) == 0)
        {
          break;
        }
        
        /* Rx interrupt. */
        
        if ((status & (RxOK | RxUnderrun | RxOverflow | RxFIFOOverrun)) != 0)
        {
          realtek_receive (device);
        }
        
        if ((status & (TxOK | TxError)) != 0) 
        {
          unsigned int dirty_tx = device->dirty_tx;
          
          while (device->current_tx - dirty_tx > 0) 
          {
            int entry = dirty_tx % NUMBER_OF_TX_DESCRIPTORS;
            int txstatus = system_port_in_u32 (port_base + TxStatus0 +
                                               entry * 4);
            
            if ((txstatus & (TxStatusOK | TxUnderrun | TxAborted)) == 0)
            {
              /* It still hasn't been transmitted. */
              
              break;
            }
            
            /* Note: TxCarrierLost is always asserted at 100 Mbps. */
            
            if ((txstatus & (TxOutOfWindow | TxAborted)) != 0)
            {
              /* There was an major error, log it. */
              
              // device->stats.tx_errors++;

              if ((txstatus & TxAborted) != 0)
              {
                // tp->stats.tx_aborted_errors++;

                system_port_out_u32 (port_base + TxConfig, 
                                     (TX_DMA_BURST << 8) | 0x03000001);
              }
              
              if ((txstatus & TxCarrierLost) != 0)
              {
                // tp->stats.tx_carrier_errors++;
              }
              
              if ((txstatus & TxOutOfWindow) != 0)
              {
                // tp->stats.tx_window_errors++;
              }
            }
            else 
            {
              if ((txstatus & TxUnderrun) != 0)
              {
                /* Add 64 to the Tx FIFO threshold. */
                
                if (device->tx_flag < 0x00300000)
                {
                  device->tx_flag += 0x00020000;
                }
                
                // tp->stats.tx_fifo_errors++;
              }
              
              // tp->stats.collisions += (txstatus >> 24) & 15;
              // tp->stats.tx_bytes += txstatus & 0x7ff;
              // tp->stats.tx_packets++;
            }
            
            // if (tp->tx_info[entry].mapping != 0)
            // {
            //   pci_unmap_single (tp->pdev,
            //                     tp->tx_info[entry].mapping,
            //                     tp->tx_info[entry].skb->len,
            //                     PCI_DMA_TODEVICE);
            // tp->tx_info[entry].mapping = 0;
            // }
          
            /* Free the original skb. */
            
            // dev_kfree_skb_irq (tp->tx_info[entry].skb);
            //          tp->tx_info[entry].skb = NULL;
            
            if (device->tx_full) 
            {
              /* The ring is no longer full, wake the queue. */
              
              device->tx_full = FALSE;
              
              // netif_wake_queue(dev);
            }
            
            dirty_tx++;
          }
          
          device->dirty_tx = dirty_tx;
        }
        
        /* Check uncommon events with one test. */
        
        if ((status & (PCIError | PCSTimeout | RxUnderrun | RxOverflow | 
                       RxFIFOOverrun | TxError | RxError)) != 0)
        {
          if (realtek_debug > 2)
          {
            log_print_formatted (&log_structure, LOG_URGENCY_WARNING,
                                 "Abnormal interrupt, status %8.8x.\n",
                                 status);
          }
          
          if (status == 0xFFFFFFFF)
          {
            break;
          }
          
          /* Update the error count. */
          
          // tp->stats.rx_missed_errors += inl(port_base + RxMissed);
          system_port_out_u32 (port_base + RxMissed, 0);
          
          if ((status & RxUnderrun) != 0 && link_changed)
          {
            /* Really link-change on new chips. */
            
            int lpar = system_port_in_u16 (port_base + NWayLPAR);
            int duplex = (lpar & 0x0100) || (lpar & 0x01C0) == 0x0040; 
            
            if (device->full_duplex != duplex) 
            {
              device->full_duplex = duplex;
              
              system_port_out_u8 (port_base + Config9346, 0xC0);
              system_port_out_u8 (port_base + Config1, 
                                  device->full_duplex ? 0x60 : 0x20);
              system_port_out_u8 (port_base + Config9346, 0x00);
            }
            status &= ~RxUnderrun;
          }
          
          if ((status & (RxUnderrun | RxOverflow | RxError |
                         RxFIFOOverrun)) != 0)
          {
            // tp->stats.rx_errors++;
          }
          
          if ((status & (PCSTimeout)) != 0)
          {
            // tp->stats.rx_length_errors++;
          }
          
          if ((status & (RxUnderrun | RxFIFOOverrun)) != 0)
          {
            //  tp->stats.rx_fifo_errors++;
          }
          
          if ((status & RxOverflow) != 0)
          {
            // tp->stats.rx_over_errors++;
            device->current_rx =
              (system_port_in_u16 (port_base + RxBufferAddress) % 
               RX_BUFFER_LENGTH);
            system_port_out_u16 (port_base + RxBufferPointer, device->current_rx - 16);
          }
          
          if ((status & PCIError) != 0) 
          {
            log_print (&log_structure, LOG_URGENCY_WARNING, "PCI Bus error.");
          }
        }
        
        if (--bogus_count < 0) 
        {
          log_print_formatted (&log_structure, LOG_URGENCY_WARNING,
                               "Too much work at interrupt, "
                               "InterruptStatus = 0x%4.4x.",
                               status);
          
          /* Clear all interrupt sources. */
          
          system_port_out_u16 (port_base + InterruptStatus, 0xFFFF);
          break;
        }
      } while (TRUE);
      
      system_call_irq_acknowledge (device->irq);
    }
  }

  /* Now, use the remaining thread for the service handler. */

  system_thread_name_set ("Service handler");

  /* Create the service. */

  if (ipc_service_create ("ethernet", &ipc_structure, &empty_tag) !=
      IPC_RETURN_SUCCESS)
  {
    log_print (&log_structure, LOG_URGENCY_EMERGENCY,
               "Couldn't create ethernet service.");
    return;
  }

  while (TRUE)
  {
    mailbox_id_type reply_mailbox_id;

    ipc_service_connection_wait (&ipc_structure);
    reply_mailbox_id = ipc_structure.output_mailbox_id;

    if (system_thread_create () == SYSTEM_RETURN_THREAD_NEW)
    {
      system_call_thread_name_set ("Handling connection");
      handle_connection (reply_mailbox_id, device);
    }
  }    
}