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

📄 at_main.c

📁 Atheros公司AR8121/AR8113无线网卡的Linux驱动
💻 C
📖 第 1 页 / 共 5 页
字号:
#ifdef NETIF_F_HW_VLAN_TX
        netdev->features = NETIF_F_SG |
                   NETIF_F_HW_CSUM |
                   NETIF_F_HW_VLAN_TX |
                   NETIF_F_HW_VLAN_RX ;
#else
        netdev->features = NETIF_F_SG | NETIF_F_HW_CSUM;
#endif

#ifdef NETIF_F_TSO
        netdev->features |= NETIF_F_TSO;
#ifdef NETIF_F_TSO6
        netdev->features |= NETIF_F_TSO6;
#endif//NETIF_F_TSO6
#endif//NETIF_F_TSO
#endif//MAX_SKB_FRAGS

#ifdef NETIF_F_LLTX
    netdev->features |= NETIF_F_LLTX;
#endif

    if(pci_using_64) {
        netdev->features |= NETIF_F_HIGHDMA;
        AT_DBG("pci using 64bit address\n");
    }
    
    /* get user settings */
    at_check_options(adapter);

    /* Init GPHY as early as possible due to power saving issue  */
    at_phy_init(&adapter->hw);
    
    
    /* reset the controller to 
     * put the device in a known good starting state */
    
    if (at_reset_hw(&adapter->hw)) {
        err = -EIO;
        goto err_reset;
    }

    /* copy the MAC address out of the EEPROM */

    at_read_mac_addr(&adapter->hw);
    memcpy(netdev->dev_addr, adapter->hw.mac_addr, netdev->addr_len);
#ifdef ETHTOOL_GPERMADDR
    memcpy(netdev->perm_addr, adapter->hw.mac_addr, netdev->addr_len);

    if (!is_valid_ether_addr(netdev->perm_addr)) {
#else
    if (!is_valid_ether_addr(netdev->dev_addr)) {
#endif
        AT_DBG("Invalid MAC Address\n");
        err = -EIO;
        goto err_eeprom;
    }
    AT_DBG("mac address : %02x-%02x-%02x-%02x-%02x-%02x\n",
        adapter->hw.mac_addr[0],
        adapter->hw.mac_addr[1],
        adapter->hw.mac_addr[2],
        adapter->hw.mac_addr[3],
        adapter->hw.mac_addr[4],
        adapter->hw.mac_addr[5] );

    init_timer(&adapter->watchdog_timer);
    adapter->watchdog_timer.function = &at_watchdog;
    adapter->watchdog_timer.data = (unsigned long) adapter;
    
    init_timer(&adapter->phy_config_timer);
    adapter->phy_config_timer.function = &at_phy_config;
    adapter->phy_config_timer.data = (unsigned long) adapter;
    
    INIT_WORK(&adapter->reset_task, at_reset_task);
    INIT_WORK(&adapter->link_chg_task, at_link_chg_task);


    strcpy(netdev->name, "eth%d"); // ??
    if((err = register_netdev(netdev)))
        goto err_register;

    /* assume we have no link for now */
    netif_carrier_off(netdev);
    netif_stop_queue(netdev);
    
    
    cards_found++;

    return 0;

//err_init_hw:
err_reset:
err_register:   
err_sw_init:
err_eeprom:
    
#ifdef CONFIG_AT_NAPI
    if (adapter->polling_netdev) {
        for (i = 0; i < adapter->num_rx_queues; i++)
            dev_put(&adapter->polling_netdev[i]);   
        kfree(adapter->polling_netdev);
    }
 #endif 
 
 #ifdef CONFIG_AT_MQ
    if (adapter->cpu_netdev) {
        free_percpu(adapter->cpu_netdev);
    }
#endif
 
    iounmap(adapter->hw.hw_addr);
err_ioremap:
    free_netdev(netdev);
err_alloc_etherdev:
    pci_release_regions(pdev);
err_pci_reg:
err_dma:
    pci_disable_device(pdev);
    return err;
}

/**
 * at_remove - Device Removal Routine
 * @pdev: PCI device information struct
 *
 * at_remove is called by the PCI subsystem to alert the driver
 * that it should release a PCI device.  The could be caused by a
 * Hot-Plug event, or because the driver is going to be removed from
 * memory.
 **/

static void __devexit
at_remove(struct pci_dev *pdev)
{
    struct net_device *netdev = pci_get_drvdata(pdev);
    struct at_adapter *adapter = netdev_priv(netdev);
#ifdef CONFIG_AT_NAPI
    int i;
#endif
    
    DEBUGFUNC("at_remove");


    /* flush_scheduled work may reschedule our watchdog task, so
     * explicitly disable watchdog tasks from being rescheduled  */
    set_bit(__AT_DOWN, &adapter->flags);

    del_timer_sync(&adapter->watchdog_timer);
    del_timer_sync(&adapter->phy_config_timer);

    flush_scheduled_work();

    unregister_netdev(netdev);

#ifdef CONFIG_AT_NAPI
    for (i = 0; i < adapter->num_rx_queues; i++)
        dev_put(&adapter->polling_netdev[i]);

    kfree(adapter->polling_netdev);
    
#endif
 
    at_force_ps(&adapter->hw);
    
    iounmap(adapter->hw.hw_addr);
    pci_release_regions(pdev);

 #ifdef CONFIG_AT_MQ
    free_percpu(adapter->cpu_netdev);//free previously allocated percpu memory  ....arg-->>pointer returned by alloc_percpu.
 #endif
 
    free_netdev(netdev);

    pci_disable_device(pdev);
}

#ifdef USE_REBOOT_NOTIFIER
/* only want to do this for 2.4 kernels? */
static int
at_notify_reboot(struct notifier_block *nb, unsigned long event, void *p)
{
    struct pci_dev *pdev = NULL;

    DEBUGFUNC("at_notify_reboot !");

    switch(event) {
    case SYS_DOWN:
    case SYS_HALT:
    case SYS_POWER_OFF:
        while((pdev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pdev))) {
            if(pci_dev_driver(pdev) == &at_driver)
                at_suspend(pdev, PMSG_SUSPEND);
        }
    }
    return NOTIFY_DONE;
    
}
#endif

#ifndef USE_REBOOT_NOTIFIER
static void at_shutdown(struct pci_dev *pdev)
{
    at_suspend(pdev, PMSG_SUSPEND);
}
#endif

#ifdef CONFIG_NET_POLL_CONTROLLER
/*
 * Polling 'interrupt' - used by things like netconsole to send skbs
 * without having to re-enable interrupts. It's not called while
 * the interrupt routine is executing.
 */
static void
at_netpoll(struct net_device *netdev)
{
    struct at_adapter *adapter = netdev_priv(netdev);

    disable_irq(adapter->pdev->irq);
    at_intr(adapter->pdev->irq, netdev);
    at_clean_tx_irq(adapter);
#ifndef CONFIG_AT_NAPI
    at_clean_rx_irq(adapter, 0);
#endif
    enable_irq(adapter->pdev->irq);
}
#endif


static int
at_suspend(struct pci_dev *pdev, pm_message_t state)
{
#define AT_SUSPEND_LINK_TIMEOUT 28
    struct net_device *netdev = pci_get_drvdata(pdev);
    struct at_adapter *adapter = netdev_priv(netdev);
    struct at_hw * hw = &adapter->hw;
    //u16 speed, duplex;
    u32 ctrl = 0;
    u32 mac_ctrl_data = 0;
    u32 wol_ctrl_data = 0;
    u16 mii_advertise_data = 0;
    u16 mii_bmsr_data = 0;
    u16 mii_intr_status_data = 0;
    u32 wufc = adapter->wol;
    u32 i;
#ifdef CONFIG_PM
    int retval = 0;
#endif

    DEBUGFUNC("at_suspend !"); 

    if (netif_running(netdev)) {
        WARN_ON(test_bit(__AT_RESETTING, &adapter->flags));
        at_down(adapter);
    }

    netif_device_detach(netdev);

#ifdef CONFIG_PM
    retval = pci_save_state(pdev);
    if (retval)
        return retval;
#endif

    if (wufc) {
        /* get link status */  
        at_read_phy_reg(hw, MII_BMSR, (u16 *)&mii_bmsr_data);
        at_read_phy_reg(hw, MII_BMSR, (u16 *)&mii_bmsr_data);
        
        mii_advertise_data = MII_AR_10T_HD_CAPS; 
        printk(KERN_DEBUG "THe mii advertise data is %x\n", mii_advertise_data);

        if ((at_write_phy_reg(hw, MII_ADVERTISE, mii_advertise_data) != 0) ||
	    (at_write_phy_reg(hw, MII_AT001_CR, 0) != 0) || 
		/* not advertise 1000M capacity */
            (at_phy_commit(hw)) != 0) { 
            printk(KERN_DEBUG "set phy register failed\n");
        }
                 
        hw->phy_configured = FALSE; /* re-init PHY when resume */
          
        /* turn on magic packet wol */
        if (wufc & AT_WUFC_MAG) {
            wol_ctrl_data |= WOL_MAGIC_EN | WOL_MAGIC_PME_EN;
        }
        if (wufc & AT_WUFC_LNKC) {
            /* if orignal link status is link, just wait for retrive link */ 
            if (mii_bmsr_data & BMSR_LSTATUS) {                 
                
                for (i=0; i < AT_SUSPEND_LINK_TIMEOUT; i++) {
                    msec_delay(100);
                    at_read_phy_reg(hw, MII_BMSR, (u16 *)&mii_bmsr_data); 
                    if (mii_bmsr_data & BMSR_LSTATUS) {
                        break;
                    }
                }
                
                if (0 == (mii_bmsr_data & BMSR_LSTATUS)) {
                    printk(KERN_DEBUG "%s: Link may change when suspend\n",
                        at_driver_name);
                }
            }
            wol_ctrl_data |=  WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN; 
            /* only link up can wake up */
            if (at_write_phy_reg(hw, 18, 0x400) != 0) {
                printk(KERN_DEBUG "%s: read write phy register failed.\n",
                        at_driver_name);
                goto wol_dis;
            }
        }
        /* clear phy interrupt */
        at_read_phy_reg(hw, 19, &mii_intr_status_data); 
        /* Config MAC Ctrl register */
        mac_ctrl_data = MAC_CTRL_RX_EN;
        /* set to 10/100M halt duplex */
        mac_ctrl_data |= MAC_CTRL_SPEED_10_100 << MAC_CTRL_SPEED_SHIFT;
        mac_ctrl_data |= (((u32)adapter->hw.preamble_len & MAC_CTRL_PRMLEN_MASK) 
                        << MAC_CTRL_PRMLEN_SHIFT);
        if (adapter->vlgrp) {
            mac_ctrl_data |= MAC_CTRL_RMV_VLAN;
        }
        if (wufc & AT_WUFC_MAG) {
            /* magic packet maybe Broadcast&multicast&Unicast frame */
            mac_ctrl_data |= MAC_CTRL_BC_EN;
        }
        AT_DBG("%s: suspend MAC=0x%x\n", at_driver_name, mac_ctrl_data);
            
        AT_WRITE_REG(hw, REG_WOL_CTRL, wol_ctrl_data);
        AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl_data);
        /* pcie patch */
        ctrl = AT_READ_REG(hw, REG_PCIE_PHYMISC);
        ctrl |= PCIE_PHYMISC_FORCE_RCV_DET;
        AT_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl);
        pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
        goto suspend_exit; 
    }    
wol_dis:
    
    /* WOL disabled */
    AT_WRITE_REG(hw, REG_WOL_CTRL, 0);
    
    /* pcie patch */
    ctrl = AT_READ_REG(hw, REG_PCIE_PHYMISC);
    ctrl |= PCIE_PHYMISC_FORCE_RCV_DET;
    AT_WRITE_REG(hw, REG_PCIE_PHYMISC, ctrl);               
    
    at_force_ps(hw);
    hw->phy_configured = FALSE; /* re-init PHY when resume */
    
    pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);

suspend_exit:   

    if (netif_running(netdev))
        at_free_irq(adapter);

    pci_disable_device(pdev);

    pci_set_power_state(pdev, pci_choose_state(pdev, state));

    return 0;
#undef AT_SUSPEND_LINK_TIMEOUT
}


#ifdef CONFIG_PM
static int
at_resume(struct pci_dev *pdev)
{
    struct net_device *netdev = pci_get_drvdata(pdev);
    struct at_adapter *adapter = netdev_priv(netdev);
    u32 err;

    DEBUGFUNC("at_resume !");

    pci_set_power_state(pdev, PCI_D0);
    pci_restore_state(pdev);
    
    if ((err = pci_enable_device(pdev))) {
        printk(KERN_ERR "ATL1e: Cannot enable PCI device from suspend\n");
        return err;
    }
    
    pci_set_master(pdev);
    
    AT_READ_REG(&adapter->hw, REG_WOL_CTRL); /* clear WOL status */

    pci_enable_wake(pdev, PCI_D3hot, 0);
    pci_enable_wake(pdev, PCI_D3cold, 0);
    
    AT_WRITE_REG(&adapter->hw, REG_WOL_CTRL, 0);
    
    if (netif_running(netdev) && (err = at_request_irq(adapter)))
        return err;

    at_reset_hw(&adapter->hw);

    if(netif_running(netdev))
        at_up(adapter);

    netif_device_attach(netdev);

    return 0;
}
#endif


#ifdef CONFIG_AT_PCI_ERS
/**
 * at_io_error_detected - called when PCI error is detected
 * @pdev: Pointer to PCI device
 * @state: The current pci connection state
 *
 * This function is called after a PCI bus error affecting
 * this device has been detected.
 */
static pci_ers_result_t at_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
{
    struct net_device *netdev = pci_get_drvdata(pdev);
    struct at_adapter *adapter = netdev->priv;

    netif_device_detach(netdev);

    if (netif_running(netdev))
        at_down(adapter);
        
    pci_disable_device(pdev);

    /* Request a slot slot reset. */
    return PCI_ERS_RESULT_NEED_RESET;
}

/**
 * at_io_slot_reset - called after the pci bus has been reset.
 * @pdev: Pointer to PCI device
 *
 * Restart the card from scratch, as if from a cold-boot. Implementation
 * resembles the first-half of the e1000_resume routine.
 */
static pci_ers_result_t at_io_slot_reset(struct pci_dev *pdev)
{
    struct net_device *netdev = pci_get_drvdata(pdev);
    struct at_adapter *adapter = netdev->priv;

    if (pci_enable_device(pdev)) {

⌨️ 快捷键说明

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