基于Atheros無線芯片的platform總線件缸、設(shè)備、驅(qū)動(dòng)叔遂、虛擬網(wǎng)橋分析(2)

3 AR9331硬件架構(gòu)

系統(tǒng)框圖

功能框圖

AR9331支持4個(gè)集成PHY的LAN口他炊,及1個(gè)集成PHY的WAN口争剿。4個(gè)LAN口通過GMII與CPU連接,WAN口可以配置使用指定的MII接口與CPU連接痊末。

地址映射

交換控制器

AR9331以太網(wǎng)交換控制器包含5個(gè)10/100M FE端口蚕苇。

AR9331集成2個(gè)GE MAC。交換芯片的phy4 WAN口通過GEO的MII接口與CPU連接凿叠。

PHY0 ~ PHY4支持橋接模式相連涩笤,在橋接模式下,GE0必須處于復(fù)位模式盒件,所有5個(gè)口都 作為LAN口蹬碧,并通過CPU的GE1與MAC0連接。如果GE0單獨(dú)連接到PHY炒刁,MAC5必須處于復(fù)位模式锰茉。

4 platform設(shè)備與驅(qū)動(dòng)

內(nèi)核初始化

通過宏MIPS_MACHINE()定義了struct mips_machine machine_ATH79_MACH_TL_WR741ND_V4_type結(jié)構(gòu)體,掛接了配置函數(shù)tl_wr741ndv4_setup()切心。宏MIPS_MACHINE()的作用是把定義的結(jié)構(gòu)體置于.mips.machines.init段中飒筑。

當(dāng)用戶配置用Kernel command line的參數(shù)board=TL-WR741ND-v4,內(nèi)核函數(shù)__setup("machtype=", mips_machtype_setup)設(shè)置設(shè)備類型mips_machtype = TL-WR741ND-v4绽昏。

內(nèi)核初始化時(shí)协屡,根據(jù)vmlinux.lds的段安排,內(nèi)核初始化函數(shù)mips_machine_setup()會(huì)遍歷.mips.machines.init段中的所有struct mips_machine結(jié)構(gòu)體成員全谤,找出設(shè)備類型為“TL-WR741ND-v4”的成員肤晓,并設(shè)置設(shè)備名稱mips_machine_name = "machine_name_ATH79_MACH_TL_WR741ND_V4",最后調(diào)用tl_wr741ndv4_setup()對設(shè)備進(jìn)行初始化配置认然。

MIPS_MACHINE(ATH79_MACH_TL_WR741ND_V4, "TL-WR741ND-v4",
         "TP-LINK TL-WR741ND v4", tl_wr741ndv4_setup);

#define MIPS_MACHINE(_type, _id, _name, _setup)                 \
static const char machine_name_##_type[] __initconst            \
                        __aligned(1) = _name;                   \
static const char machine_id_##_type[] __initconst              \
                        __aligned(1) = _id;                     \
static struct mips_machine machine_##_type                      \
                __used __section(.mips.machines.init) =         \
{                                                               \
        .mach_type      = _type,                                \
        .mach_id        = machine_id_##_type,                   \
        .mach_name      = machine_name_##_type,                 \
        .mach_setup     = _setup,                               \
};

/* vmlinux.lds */
 .mips.machines.init : AT(ADDR(.mips.machines.init) - 0) {
  __mips_machines_start = .;
  *(.mips.machines.init)
  __mips_machines_end = .;

注冊platform設(shè)備

platform設(shè)備注冊的接口是platform_device_register()补憾。

AR9331芯片寄存器描述:

#define AR71XX_APB_BASE        0x18000000
#define AR933X_GMAC_BASE    (AR71XX_APB_BASE + 0x00070000)

/*
 * AR933X GMAC interface
 */
#define AR933X_GMAC_REG_ETH_CFG        0x00
#define AR933X_ETH_CFG_SW_PHY_SWAP    BIT(7)
#define AR933X_ETH_CFG_SW_PHY_ADDR_SWAP    BIT(8)
static void __init tl_wr741ndv4_setup(void)
{
    u8 *mac = (u8 *) KSEG1ADDR(0x1f01fc00);
    u8 *ee = (u8 *) KSEG1ADDR(0x1fff1000);

    /* 寄存器ETH_CFG的第7、第8位卷员,置位 */
    ath79_setup_ar933x_phy4_switch(true, true);

    /* 清除寄存器GPIO_FUNCTION_1的第3~7位 */
    ath79_gpio_function_disable(AR933X_GPIO_FUNC_ETH_SWITCH_LED0_EN |
                    AR933X_GPIO_FUNC_ETH_SWITCH_LED1_EN |
                    AR933X_GPIO_FUNC_ETH_SWITCH_LED2_EN |
                    AR933X_GPIO_FUNC_ETH_SWITCH_LED3_EN |
                    AR933X_GPIO_FUNC_ETH_SWITCH_LED4_EN);

    ath79_register_leds_gpio(-1, ARRAY_SIZE(tl_wr741ndv4_leds_gpio),
                 tl_wr741ndv4_leds_gpio);

    ath79_register_gpio_keys_polled(1, TL_WR741NDV4_KEYS_POLL_INTERVAL,
                    ARRAY_SIZE(tl_wr741ndv4_gpio_keys),
                    tl_wr741ndv4_gpio_keys);

    ath79_register_m25p80(&tl_wr741ndv4_flash_data);

    /* 設(shè)置eth0與eth1的mac地址盈匾,分別是mac+1與mac-1 */
    ath79_init_mac(ath79_eth0_data.mac_addr, mac, 1);
    ath79_init_mac(ath79_eth1_data.mac_addr, mac, -1);

    /* 掛接mdio驅(qū)動(dòng),注冊mdio設(shè)備毕骡。
       mdio_dev = &ath79_mdio1_device;
       mdio_data = &ath79_mdio1_data; 且mdio_data->builtin_switch置位 */
    ath79_register_mdio(0, 0x0);

    /* 注冊eth設(shè)備 */
    ath79_register_eth(1);
    ath79_register_eth(0);

    /* 注冊wmac設(shè)備 */
    ath79_register_wmac(ee, mac);
}

/* ath79_setup_ar933x_phy4_switch()`設(shè)置`ETH_CFG`寄存器的第7削饵、第8位 */
void __init ath79_setup_ar933x_phy4_switch(bool mac, bool mdio)
{
    void __iomem *base;
    u32 t;

    base = ioremap(AR933X_GMAC_BASE, AR933X_GMAC_SIZE);

    t = __raw_readl(base + AR933X_GMAC_REG_ETH_CFG);
    t &= ~(AR933X_ETH_CFG_SW_PHY_SWAP | AR933X_ETH_CFG_SW_PHY_ADDR_SWAP);
    if (mac)
        t |= AR933X_ETH_CFG_SW_PHY_SWAP;
    if (mdio)
        t |= AR933X_ETH_CFG_SW_PHY_ADDR_SWAP;
    __raw_writel(t, base + AR933X_GMAC_REG_ETH_CFG);

    iounmap(base);
}

ath79_init_mac()設(shè)置mac地址。

void __init ath79_init_mac(unsigned char *dst, const unsigned char *src,
                int offset)
{
    int t;

    if (!dst)
        return;

    if (!src || !is_valid_ether_addr(src)) {
        memset(dst, '\0', ETH_ALEN);
        return;
    }

    t = (((u32) src[3]) << 16) + (((u32) src[4]) << 8) + ((u32) src[5]);
    t += offset;

    dst[0] = src[0];
    dst[1] = src[1];
    dst[2] = src[2];
    dst[3] = (t >> 16) & 0xff;
    dst[4] = (t >> 8) & 0xff;
    dst[5] = t & 0xff;
}

ath79_register_eth()注冊eth設(shè)備未巫。

struct platform_device ath79_eth0_device = {
    .name        = "ag71xx",
    .id        = 0,
    .resource    = ath79_eth0_resources,
    .num_resources    = ARRAY_SIZE(ath79_eth0_resources),
    .dev = {
        .platform_data = &ath79_eth0_data,
    },
};

void __init ath79_register_eth(unsigned int id)
{
    struct platform_device *pdev;
    struct ag71xx_platform_data *pdata;
    int err;

    if (id > 1) {
        printk(KERN_ERR "ar71xx: invalid ethernet id %d\n", id);
        return;
    }

    /* 初始化ath79_ethX_pll_data */
    ath79_init_eth_pll_data(id);

    if (id == 0)
        pdev = &ath79_eth0_device;
    else
        pdev = &ath79_eth1_device;

    /* 設(shè)置ath79_eth0_device.dev.platform_data.phy_if_mode =  
                                                    PHY_INTERFACE_MODE_MII窿撬。
          ath79_eth0_device.dev.platform_data.phy_if_mode = 
                                                    PHY_INTERFACE_MODE_GMII */
    pdata = pdev->dev.platform_data;
    err = ath79_setup_phy_if_mode(id, pdata);
    if (err) {
        printk(KERN_ERR
               "ar71xx: invalid PHY interface mode for GE%u\n", id);
        return;
    }

    switch (ath79_soc) {
    case ATH79_SOC_AR9331:
        if (id == 0) {
            pdata->reset_bit = AR933X_RESET_GE0_MAC |
                       AR933X_RESET_GE0_MDIO;
            pdata->ddr_flush = ar933x_ddr_flush_ge0;
            pdata->set_speed = ath79_set_speed_dummy;

            pdata->phy_mask = BIT(4);
        } else {
            pdata->reset_bit = AR933X_RESET_GE1_MAC |
                       AR933X_RESET_GE1_MDIO;
            pdata->ddr_flush = ar933x_ddr_flush_ge1;
            pdata->set_speed = ath79_set_speed_dummy;

            pdata->speed = SPEED_1000;
            pdata->duplex = DUPLEX_FULL;
            pdata->switch_data = &ath79_switch_data;

            ath79_switch_data.phy_poll_mask |= BIT(4);
        }

        pdata->has_gbit = 1;
        pdata->is_ar724x = 1;

        if (!pdata->fifo_cfg1)
            pdata->fifo_cfg1 = 0x0010ffff;
        if (!pdata->fifo_cfg2)
            pdata->fifo_cfg2 = 0x015500aa;
        if (!pdata->fifo_cfg3)
            pdata->fifo_cfg3 = 0x01f00140;
        break;
    }

    switch (pdata->phy_if_mode) {
    case PHY_INTERFACE_MODE_GMII:
    case PHY_INTERFACE_MODE_RGMII:
        if (!pdata->has_gbit) {
            printk(KERN_ERR "ar71xx: no gbit available on eth%d\n",
                    id);
            return;
        }
        /* fallthrough */
    default:
        break;
    }

    if (!is_valid_ether_addr(pdata->mac_addr)) {
        random_ether_addr(pdata->mac_addr);
        printk(KERN_DEBUG
            "ar71xx: using random MAC address for eth%d\n",
            ath79_eth_instance);
    }

    if (pdata->mii_bus_dev == NULL) {
        switch (ath79_soc) {
        case ATH79_SOC_AR9331:
            pdata->mii_bus_dev = &ath79_mdio1_device.dev;
            break;
        }
    }

    /* 設(shè)置寄存器RST_RESET,重啟MAC與MDIO叙凡,寄存器進(jìn)入復(fù)位狀態(tài) */
    ath79_device_reset_set(pdata->reset_bit);
    mdelay(100);

    /* 解除重啟劈伴,進(jìn)入正常工作 */
    ath79_device_reset_clear(pdata->reset_bit);
    mdelay(100);

    /* 注冊platform設(shè)備dev */
    platform_device_register(pdev);
    ath79_eth_instance++;
}

int platform_device_register(struct platform_device *pdev)
{
    /* pdev->dev->kobj.kset = devices_kset */
    device_initialize(&pdev->dev);

    arch_setup_pdev_archdata(pdev);
    
    platform_device_add(pdev);
}

int platform_device_add(struct platform_device *pdev)
{
    /* 關(guān)聯(lián)platform設(shè)備框架 */
    if (!pdev->dev.parent)
        pdev->dev.parent = &platform_bus;

    /* 關(guān)聯(lián)platform總線框架 */
    pdev->dev.bus = &platform_bus_type;

    /* 設(shè)置pdev->dev.kobj.name = "pdev->name"."pdev->id" */
    dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);

    device_add(&pdev->dev);
}

ath79_register_wmac()注冊wmac設(shè)備。

void __init ath79_register_wmac(u8 *cal_data, u8 *mac_addr)
{
    ar933x_wmac_setup();

    if (cal_data)
        memcpy(ath79_wmac_data.eeprom_data, cal_data,
               sizeof(ath79_wmac_data.eeprom_data));

    if (mac_addr) {
        memcpy(ath79_wmac_mac, mac_addr, sizeof(ath79_wmac_mac));
        ath79_wmac_data.macaddr = ath79_wmac_mac;
    }

    /* 注冊platform設(shè)備ath79_wmac_device */
    platform_device_register(&ath79_wmac_device);
}

static void __init ar933x_wmac_setup(void)
{
    u32 t;

    /* 設(shè)置寄存器RST_RESET.WLAN_RESET握爷,使WLAN重啟 */
    ar933x_wmac_reset();

    ath79_wmac_device.name = "ar933x_wmac";

    /* 設(shè)置wmac的IORESOURCE_MEM為0x18100000 ~ 0x1811ffff
        MAC_PCU – 1810_8000
        RTC – 1810_7000
        Host Interface – 1810_4000
        DCU – 1810_1000
        QCU – 1810_0800
        DMA – 1810_0000
       wmac的IORESOURCE_IRQ = 2 */
    ath79_wmac_resources[0].start = AR933X_WMAC_BASE;
    ath79_wmac_resources[0].end = AR933X_WMAC_BASE + AR933X_WMAC_SIZE - 1;
    ath79_wmac_resources[1].start = ATH79_CPU_IRQ_IP2;
    ath79_wmac_resources[1].end = ATH79_CPU_IRQ_IP2;

    t = ath79_reset_rr(AR933X_RESET_REG_BOOTSTRAP);
    if (t & AR933X_BOOTSTRAP_REF_CLK_40)
        ath79_wmac_data.is_clk_25mhz = false;
    else
        ath79_wmac_data.is_clk_25mhz = true;

    if (ath79_soc_rev == 1)
        ath79_wmac_data.get_mac_revision = ar933x_r1_get_wmac_revision;

    ath79_wmac_data.external_reset = ar933x_wmac_reset;
}

注冊platform驅(qū)動(dòng)

platform驅(qū)動(dòng)注冊的接口是platform_driver_register()跛璧。

static struct platform_driver ag71xx_driver = {
    .probe        = ag71xx_probe,
    .remove        = __exit_p(ag71xx_remove),
    .driver = {
        .name    = "ag71xx",
    }
};

static int __init ag71xx_module_init(void)
{
    platform_driver_register(&ag71xx_driver);
}

#define platform_driver_register(drv) \
    __platform_driver_register(drv, THIS_MODULE)

int __platform_driver_register(struct platform_driver *drv,
                struct module *owner)
{
    drv->driver.owner = owner;
    drv->driver.bus = &platform_bus_type;
    /* 如果驅(qū)動(dòng)對象存在probe()等函數(shù)严里,則設(shè)置驅(qū)動(dòng)對象的驅(qū)動(dòng)成員
       (struct platform_driver.driver)的probe等指針。
       這樣包裝的目的:當(dāng)內(nèi)核設(shè)備驅(qū)動(dòng)框架進(jìn)行驅(qū)動(dòng)掛接時(shí)赡模,先調(diào)用platform_drv_probe()等
       函數(shù)田炭,這些函數(shù)會(huì)做一些額外的檢查與保護(hù),然后調(diào)用到實(shí)際的驅(qū)動(dòng)probe()函數(shù) */
    if (drv->probe)
        drv->driver.probe = platform_drv_probe;
    if (drv->remove)
        drv->driver.remove = platform_drv_remove;
    if (drv->shutdown)
        drv->driver.shutdown = platform_drv_shutdown;

    driver_register(&drv->driver);
}

int driver_register(struct device_driver *drv)
{
    /* 查找驅(qū)動(dòng)是否已注冊 */
    driver_find(drv->name, drv->bus);
    /*  */
    bus_add_driver(drv);

    driver_add_groups(drv, drv->groups);

    kobject_uevent(&drv->p->kobj, KOBJ_ADD);

}

/* 從platform總線框架里查找驅(qū)動(dòng)名稱是否已經(jīng)注冊漓柑。查找方式是查找priv.drivers_kset
   保存的已注冊驅(qū)動(dòng)鏈表 */
struct device_driver *driver_find(const char *name, struct bus_type *bus)
{
    struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
    struct driver_private *priv;

    if (k) {
        /* Drop reference added by kset_find_obj() */
        kobject_put(k);
        priv = to_driver(k);
        return priv->driver;
    }
    return NULL;
}

int bus_add_driver(struct device_driver *drv)
{
    struct bus_type *bus;
    struct driver_private *priv;
    int error = 0;

    priv = kzalloc(sizeof(*priv), GFP_KERNEL);

    klist_init(&priv->klist_devices, NULL, NULL);
    /* 關(guān)聯(lián)私有數(shù)據(jù)結(jié)構(gòu)體 */
    priv->driver = drv;
    drv->p = priv;
    priv->kobj.kset = bus->p->drivers_kset;
    /* 設(shè)置priv.kobj.name */
    kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
             "%s", drv->name);
    /* 把新的驅(qū)動(dòng)節(jié)點(diǎn)添加到總線的驅(qū)動(dòng)維護(hù)鏈表里 */
    klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
    /* 如果設(shè)置了自動(dòng)探測標(biāo)志教硫,則進(jìn)行設(shè)備的探測與驅(qū)動(dòng)的掛接加載。如果設(shè)備在驅(qū)動(dòng)之前注冊
       的辆布,那么就可以探測到瞬矩;反之亦然,在注冊設(shè)備時(shí)锋玲,也會(huì)執(zhí)行這樣的類似操作景用,進(jìn)行驅(qū)動(dòng)的
       探測。所以惭蹂,不管是先注冊設(shè)備伞插,還是先注冊驅(qū)動(dòng),在注冊時(shí)部是會(huì)進(jìn)行探測匹配 */
    if (drv->bus->p->drivers_autoprobe) {
        driver_attach(drv);
    }
    module_add_driver(drv->owner, drv);

    error = driver_create_file(drv, &driver_attr_uevent);
    if (error) {
        printk(KERN_ERR "%s: uevent attr (%s) failed\n",
            __func__, drv->name);
    }
    error = driver_add_groups(drv, bus->drv_groups);
    if (error) {
        /* How the hell do we get out of this pickle? Give up */
        printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",
            __func__, drv->name);
    }

    if (!drv->suppress_bind_attrs) {
        error = add_bind_files(drv);
        if (error) {
            /* Ditto */
            printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
                __func__, drv->name);
        }
    }

    return 0;

out_unregister:
    kobject_put(&priv->kobj);
    kfree(drv->p);
    drv->p = NULL;
out_put_bus:
    bus_put(bus);
    return error;
}

/* 探測設(shè)備盾碗,掛接驅(qū)動(dòng) */
int driver_attach(struct device_driver *drv)
{
    return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}

int bus_for_each_dev(struct bus_type *bus, struct device *start,
             void *data, int (*fn)(struct device *, void *))
{
    struct klist_iter i;
    struct device *dev;

    /* 把i->i_klist指向總線的設(shè)備維護(hù)鏈表klist_devices */
    klist_iter_init_node(&bus->p->klist_devices, &i,
                 (start ? &start->p->knode_bus : NULL));
    /* 迭代鏈表klist_devices里的所有節(jié)點(diǎn)媚污,執(zhí)行__driver_attach()函數(shù)。
       dev指向結(jié)構(gòu)體struct platform_device.dev廷雅,本例中是設(shè)備ath79_eth0_device.dev耗美。
       data指向結(jié)構(gòu)體struct platform_driver.device_driver,本例子中是
       驅(qū)動(dòng)ag71xx_driver.driver航缀。 */
    while ((dev = next_device(&i)) && !error)
        error = fn(dev, data);
    klist_iter_exit(&i);
}

static int __driver_attach(struct device *dev, void *data)
{
    struct device_driver *drv = data;
    
    /* 執(zhí)行platform_bus_type.match() */
    if (!driver_match_device(drv, dev))
        return 0;

    if (dev->parent)    /* Needed for USB */
        device_lock(dev->parent);
    device_lock(dev);
    /* 如果設(shè)備還未掛接過驅(qū)動(dòng)商架,現(xiàn)在掛接驅(qū)動(dòng) */
    if (!dev->driver)
        driver_probe_device(drv, dev);
    device_unlock(dev);
    if (dev->parent)
        device_unlock(dev->parent);

    return 0;
}

static int platform_match(struct device *dev, struct device_driver *drv)
{
    struct platform_device *pdev = to_platform_device(dev);
    struct platform_driver *pdrv = to_platform_driver(drv);

    /* When driver_override is set, only bind to the matching driver */
    if (pdev->driver_override)
        return !strcmp(pdev->driver_override, drv->name);

    /* Attempt an OF style match first */
    if (of_driver_match_device(dev, drv))
        return 1;

    /* Then try ACPI style match */
    if (acpi_driver_match_device(dev, drv))
        return 1;

    /* Then try to match against the id table */
    if (pdrv->id_table)
        return platform_match_id(pdrv->id_table, pdev) != NULL;

    /* 對于本例,以上匹配規(guī)則都不會(huì)被執(zhí)行芥玉。
       設(shè)備名與驅(qū)動(dòng)名都是"ag71xx"蛇摸,因此返回匹配。這里的設(shè)備名不是指在/sys/下看到的設(shè)備
       名飞傀,/sys/下的設(shè)備名稱是"ag71xx.0" */
    return (strcmp(pdev->name, drv->name) == 0);
}

int driver_probe_device(struct device_driver *drv, struct device *dev)
{
    int ret = 0;

    /* 判斷設(shè)備是否已經(jīng)注冊皇型。state_in_sysfs標(biāo)志 */
    if (!device_is_registered(dev))
        return -ENODEV;

    pm_runtime_barrier(dev);
    /* 執(zhí)行探測函數(shù) */
    really_probe(dev, drv);
    pm_request_idle(dev);
}

static int really_probe(struct device *dev, struct device_driver *drv)
{
    /* 掛接驅(qū)動(dòng),ath79_eth0_device.dev.driver = &ag71xx_driver.driver */
    dev->driver = drv;
    /* 設(shè)置用戶空間sysfs的驅(qū)動(dòng)與設(shè)備的掛接軟鏈接 */
    driver_sysfs_add(dev);

    if (dev->bus->probe) {
        dev->bus->probe(dev);
    } else if (drv->probe) { /* 執(zhí)行這里 */
        /* 執(zhí)行ag71xx_driver.driver.probe()砸烦,即platform_drv_probe()。
           platform_drv_probe()會(huì)做一些檢查與保護(hù)绞吁,并調(diào)用到真正的驅(qū)動(dòng)probe()函數(shù)幢痘,
           ag71xx_driver.probe(),即ag71xx_probe() */
        drv->probe(dev); 
    }

    driver_bound(dev);
    ret = 1;
    pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
         drv->bus->name, __func__, dev_name(dev), drv->name);
    goto done;

done:
    atomic_dec(&probe_count);
    wake_up(&probe_waitqueue);
    return ret;
}

/* 創(chuàng)建軟鏈接家破。sysfs是分層結(jié)構(gòu)組織的颜说,設(shè)備放在設(shè)備目錄下购岗,驅(qū)動(dòng)放在驅(qū)動(dòng)目錄下,那么在用戶
   空間設(shè)備目錄與驅(qū)動(dòng)目錄是如何掛接呢门粪?技巧就是使用軟鏈接喊积,在設(shè)備目錄軟鏈接指向?qū)?yīng)的驅(qū)動(dòng)
   ,在驅(qū)動(dòng)目錄下軟鏈接指向?qū)?yīng)的設(shè)備玄妈。 */
static int driver_sysfs_add(struct device *dev)
{
    int ret;

    /* 這個(gè)是在創(chuàng)建軟件鏈接乾吻,在dev->driver->p->kobj(即設(shè)備所掛接的驅(qū)動(dòng)的私有數(shù)據(jù)結(jié)構(gòu)
       體的kobj)對象下創(chuàng)建以設(shè)備名稱(即ath79_eth0_device.dev.kobj.name)命名的軟鏈
       接,指向設(shè)備的sysfs目錄:
       /sys/bus/platform/drivers/ag71xx/ag71xx.0 -> 
                                            /sys/devices/platform/ag71xx.0 */
    ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
              kobject_name(&dev->kobj));
    if (ret == 0) {
        /* 創(chuàng)建軟鏈接: /sys/devices/platform/ag71xx.0/driver ->
                                         /sys/bus/platform/drivers/ag71xx/ */
        sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
              "driver");
    }
}

struct net

linux每個(gè)進(jìn)程有自己的命名空間拟蜻,在clone進(jìn)程時(shí)绎签,為新進(jìn)程分配命名空間。命名空間由全局變量init_nsproxy定義酝锅,按功能模塊進(jìn)行獨(dú)立管理诡必,內(nèi)核可配置選擇是否使用網(wǎng)絡(luò)命名空間用來管理所有的網(wǎng)絡(luò)設(shè)備與驅(qū)動(dòng),初始化入口是net_dev_init()搔扁。如果不使用命名空間爸舒,則每個(gè)新進(jìn)程分配的命名空間都是使用父進(jìn)程的命名空間,即所有進(jìn)程都屬于同一命名空間稿蹲,網(wǎng)絡(luò)命名空間是全局變量init_net扭勉;如果使用命名空間,則每個(gè)新進(jìn)程分配的命名空間是重新創(chuàng)建的命名空間场绿,由鏈表net_namespace_list管理所有的網(wǎng)絡(luò)命名空間剖效,第一個(gè)進(jìn)程的網(wǎng)絡(luò)命名空間仍然是init_net

static int __init net_dev_init(void)
{
    dev_proc_init(); /* 在procfs下創(chuàng)建管理節(jié)點(diǎn) */

    netdev_kobject_init(); /* 賦值kobj_ns_ops_tbl[KOBJ_NS_TYPE_NET] = 
                    &net_ns_type_operations焰盗。
                    創(chuàng)建/sys/class/net/璧尸。 */

    INIT_LIST_HEAD(&ptype_all);
    for (i = 0; i < PTYPE_HASH_SIZE; i++)
        INIT_LIST_HEAD(&ptype_base[i]);

    INIT_LIST_HEAD(&offload_base);

    if (register_pernet_subsys(&netdev_net_ops))
        goto out;

    dev_boot_phase = 0;

    /* 把first_device指針指向loopback_net_ops.list */
    register_pernet_device(&loopback_net_ops); /* loopback_net_ops(&init_net) */
    register_pernet_device(&default_device_ops);

    /* 設(shè)置軟中斷softirq_vec[].action */
    open_softirq(NET_TX_SOFTIRQ, net_tx_action);
    open_softirq(NET_RX_SOFTIRQ, net_rx_action);

    hotcpu_notifier(dev_cpu_callback, 0);
    dst_init(); /* 注冊網(wǎng)絡(luò)通告br_init */
}

static int __net_init netdev_init(struct net *net)
{
    /* init_net->dev_base_head在定義變量時(shí)就已經(jīng)初始化了。
       dev_base_head用來把所有設(shè)備的struct net_device鏈接起來熬拒。 */
    if (net != &init_net)
        INIT_LIST_HEAD(&net->dev_base_head);
    /* 分配256個(gè)struct hlist_node大小的空間 */
    net->dev_name_head = netdev_create_hash();
    /* 分配256個(gè)struct hlist_node大小的空間 */
    net->dev_index_head = netdev_create_hash();
}

static struct pernet_operations __net_initdata netdev_net_ops = {
    .init = netdev_init,
    .exit = netdev_exit,
};

int register_pernet_subsys(struct pernet_operations *ops)
{
    register_pernet_operations(first_device, ops);
}        

static int register_pernet_operations(struct list_head *list,
                      struct pernet_operations *ops)
{
    __register_pernet_operations(list, ops);
}

/* 沒有網(wǎng)絡(luò)命名空間的情況下 */
static int __register_pernet_operations(struct list_head *list,
                    struct pernet_operations *ops)
{
    return ops_init(ops, &init_net);
}

static int ops_init(const struct pernet_operations *ops, struct net *net)
{
    ops->init(net);
}

static struct pernet_operations __net_initdata dev_proc_ops = {
    .init = dev_proc_net_init,
    .exit = dev_proc_net_exit,
};

static struct pernet_operations __net_initdata dev_mc_net_ops = {
    .init = dev_mc_net_init,
    .exit = dev_mc_net_exit,
};

int __init dev_proc_init(void)
{
    register_pernet_subsys(&dev_proc_ops); /* 執(zhí)行dev_proc_net_init(&init_net) */
    register_pernet_subsys(&dev_mc_net_ops); /* 執(zhí)行dev_mc_net_init(&init_net) */
}

int __init netdev_kobject_init(void)
{
    /* 賦值kobj_ns_ops_tbl[KOBJ_NS_TYPE_NET] = &net_ns_type_operations */
    kobj_ns_type_register(&net_ns_type_operations);
    /* 創(chuàng)建/sys/class/net/ */ 
    return class_register(&net_class);
}
```c

**驅(qū)動(dòng)探測**

```c
static int __devinit ag71xx_probe(struct platform_device *pdev)
{
    struct net_device *dev;
    struct resource *res;
    struct ag71xx *ag;
    struct ag71xx_platform_data *pdata;
    int err;
    /* ath79_eth0_data */
    pdata = pdev->dev.platform_data;

    /* 設(shè)備注冊時(shí)已經(jīng)初始化爷光,pdata->mii_bus_dev = &ath79_mdio1_device.dev */
    if (pdata->mii_bus_dev == NULL) {
        dev_err(&pdev->dev, "no MII bus device specified\n");
        err = -EINVAL;
        goto err_out;
    }

    /* 創(chuàng)建eth設(shè)備的net_device */
    dev = alloc_etherdev(sizeof(*ag));

    SET_NETDEV_DEV(dev, &pdev->dev);

    ag = netdev_priv(dev);
    ag->pdev = pdev;
    ag->dev = dev;
    ag->msg_enable = netif_msg_init(ag71xx_msg_level,
                    AG71XX_DEFAULT_MSG_ENABLE);
    spin_lock_init(&ag->lock);

    res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mac_base");

    ag->mac_base = ioremap_nocache(res->start, res->end - res->start + 1);

    dev->irq = platform_get_irq(pdev, 0);
    err = request_irq(dev->irq, ag71xx_interrupt,
              IRQF_DISABLED,
              dev->name, dev);

    dev->base_addr = (unsigned long)ag->mac_base;
    dev->netdev_ops = &ag71xx_netdev_ops;
    dev->ethtool_ops = &ag71xx_ethtool_ops;

    INIT_WORK(&ag->restart_work, ag71xx_restart_work_func);

    init_timer(&ag->oom_timer);
    ag->oom_timer.data = (unsigned long) dev;
    ag->oom_timer.function = ag71xx_oom_timer_handler;

    ag->tx_ring.size = AG71XX_TX_RING_SIZE_DEFAULT;
    ag->rx_ring.size = AG71XX_RX_RING_SIZE_DEFAULT;

    ag->stop_desc = dma_alloc_coherent(NULL,
        sizeof(struct ag71xx_desc), &ag->stop_desc_dma, GFP_KERNEL);

    if (!ag->stop_desc)
        goto err_free_irq;

    ag->stop_desc->data = 0;
    ag->stop_desc->ctrl = 0;
    ag->stop_desc->next = (u32) ag->stop_desc_dma;

    memcpy(dev->dev_addr, pdata->mac_addr, ETH_ALEN);

    netif_napi_add(dev, &ag->napi, ag71xx_poll, AG71XX_NAPI_WEIGHT);

    register_netdev(dev);
    
    /* 打印Ethernet寄存器信息 */
    ag71xx_dump_regs(ag);

    ag71xx_hw_init(ag);

    ag71xx_dump_regs(ag);

    ag71xx_phy_connect(ag);

    ag71xx_debugfs_init(ag);

    platform_set_drvdata(pdev, dev);

    return 0;
}

分配網(wǎng)絡(luò)設(shè)備實(shí)例并注冊

#define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1)
#define alloc_etherdev_mq(sizeof_priv, count) alloc_etherdev_mqs(sizeof_priv,
                 count, count)

struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs,
                      unsigned int rxqs)
{
    return alloc_netdev_mqs(sizeof_priv, "eth%d", NET_NAME_UNKNOWN,
                ether_setup, txqs, rxqs);
}

/* @sizeof_priv:私有數(shù)據(jù),比如設(shè)備的特征信息等澎粟,用戶可根據(jù)需要添加任意的結(jié)構(gòu)體蛀序。
   @setup():初始化設(shè)備的回調(diào)函數(shù)ether_setup()
   @txqs:TX隊(duì)列的個(gè)數(shù)
   @rxqs:RX隊(duì)列的個(gè)數(shù)
*/    
struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
        unsigned char name_assign_type,
        void (*setup)(struct net_device *),
        unsigned int txqs, unsigned int rxqs)
{
    struct net_device *dev;
    size_t alloc_size;
    struct net_device *p;

    /* 在連續(xù)內(nèi)存空間分配兩個(gè)結(jié)構(gòu)體,net_device與struct ag71xx */
    alloc_size = sizeof(struct net_device);
    if (sizeof_priv) {        
        alloc_size += sizeof_priv;
    }

    dev = kzalloc(alloc_size, GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT);
    dev->pcpu_refcnt = alloc_percpu(int);
    dev_addr_init(dev); /* 初始化設(shè)備mac的mac地址鏈表 */
    dev_mc_init(dev); /* 初始化多播轉(zhuǎn)發(fā)mac地址鏈表 */
    dev_uc_init(dev); /* 初始化單播轉(zhuǎn)發(fā)mac地址鏈表 */
    /* 關(guān)聯(lián)命名空間活烙,dev->nd_net = &init_net */
    dev_net_set(dev, &init_net);  

    /* 設(shè)置GSO */
    dev->gso_max_size = GSO_MAX_SIZE;
    dev->gso_max_segs = GSO_MAX_SEGS;
    dev->gso_min_segs = 0;

    INIT_LIST_HEAD(&dev->napi_list);
    INIT_LIST_HEAD(&dev->unreg_list);
    INIT_LIST_HEAD(&dev->close_list);
    INIT_LIST_HEAD(&dev->link_watch_list);
    INIT_LIST_HEAD(&dev->adj_list.upper);
    INIT_LIST_HEAD(&dev->adj_list.lower);
    INIT_LIST_HEAD(&dev->all_adj_list.upper);
    INIT_LIST_HEAD(&dev->all_adj_list.lower);
    INIT_LIST_HEAD(&dev->ptype_all);
    INIT_LIST_HEAD(&dev->ptype_specific);
    dev->priv_flags = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM;
    setup(dev); /* 執(zhí)行ether_setup() */

    dev->num_tx_queues = txqs; /* =1 */
    dev->real_num_tx_queues = txqs; /* =1 */
    /* netif_alloc_netdev_queues()創(chuàng)建并初始化發(fā)包隊(duì)列徐裸,并把隊(duì)列掛接到`dev`設(shè)備上 */
    netif_alloc_netdev_queues(dev);
#ifdef CONFIG_SYSFS
    dev->num_rx_queues = rxqs; /* =1 */
    dev->real_num_rx_queues = rxqs; /* =1 */
    /* netif_alloc_rx_queues()創(chuàng)建并初始化收包隊(duì)列,并把隊(duì)列掛接到`dev`設(shè)備上 */
    netif_alloc_rx_queues(dev);
#endif
    strcpy(dev->name, name);
    dev->name_assign_type = name_assign_type;
    dev->group = INIT_NETDEV_GROUP;
    if (!dev->ethtool_ops)
        dev->ethtool_ops = &default_ethtool_ops;
    return dev;
}

void ether_setup(struct net_device *dev)
{
    dev->header_ops        = &eth_header_ops;
    dev->type        = ARPHRD_ETHER;
    dev->hard_header_len     = ETH_HLEN;
    dev->mtu        = ETH_DATA_LEN;
    dev->addr_len        = ETH_ALEN;
    dev->tx_queue_len    = 1000;    /* Ethernet wants good queues */
    dev->flags        = IFF_BROADCAST|IFF_MULTICAST;
    dev->priv_flags        |= IFF_TX_SKB_SHARING;

    memset(dev->broadcast, 0xFF, ETH_ALEN);
}

/* 注冊net_device設(shè)備 */
int register_netdev(struct net_device *dev)
{
    rtnl_lock();
    err = register_netdevice(dev);
    rtnl_unlock();
}

int register_netdevice(struct net_device *dev)
{
    int ret;
    struct net *net = dev_net(dev);

    BUG_ON(dev_boot_phase); /* net_dev_init()設(shè)置dev_boot_phase = 0 */

    /* When net_device's are persistent, this will be fatal. */
    BUG_ON(dev->reg_state != NETREG_UNINITIALIZED);
    BUG_ON(!net);

    spin_lock_init(&dev->addr_list_lock);
    netdev_set_addr_lockdep_class(dev);

    dev->iflink = -1;

    /* 如果名稱是eth%d這種帶有未決字符的啸盏,搜索未使用的索引進(jìn)行賦值重贺。
       搜索方式:從命名空間的設(shè)備名鏈表struct net.dev_base_head里從0開始搜索
       還未使用的最小索引值。 */
    dev_get_valid_name(net, dev, dev->name);

    /* dev->netdev_ops = &ag71xx_netdev_ops */
    if (dev->netdev_ops->ndo_init) { /* NULL,不執(zhí)行 */
        dev->netdev_ops->ndo_init(dev); 
    }
    if (((dev->hw_features | dev->features) &
         NETIF_F_HW_VLAN_CTAG_FILTER) &&
        (!dev->netdev_ops->ndo_vlan_rx_add_vid ||
         !dev->netdev_ops->ndo_vlan_rx_kill_vid)) {
        netdev_WARN(dev, "Buggy VLAN acceleration in driver!\n");
        ret = -EINVAL;
        goto err_uninit;
    }

    ret = -EBUSY;
    if (!dev->ifindex)
        dev->ifindex = dev_new_index(net); /* 分配一個(gè)ifindex气笙,取net->ifindex的值
                            作為基值次企,與dev->index_hlist鏈表進(jìn)行比較,如果已存在潜圃,則
                            把基值+1遞增繼續(xù)比較缸棵,直到找出未使用的ifindex則返回。
                            ifindex的值最小為1谭期。 */
    else if (__dev_get_by_index(net, dev->ifindex))
        goto err_uninit;

    if (dev->iflink == -1)
        dev->iflink = dev->ifindex;

    /* Transfer changeable features to wanted_features and enable
     * software offloads (GSO and GRO).
     */
    dev->hw_features |= NETIF_F_SOFT_FEATURES;
    dev->features |= NETIF_F_SOFT_FEATURES;
    dev->wanted_features = dev->features & dev->hw_features;

    if (!(dev->flags & IFF_LOOPBACK)) {
        dev->hw_features |= NETIF_F_NOCACHE_COPY;
    }

    /* Make NETIF_F_HIGHDMA inheritable to VLAN devices.
     */
    dev->vlan_features |= NETIF_F_HIGHDMA;

    /* Make NETIF_F_SG inheritable to tunnel devices.
     */
    dev->hw_enc_features |= NETIF_F_SG;

    /* Make NETIF_F_SG inheritable to MPLS.
     */
    dev->mpls_features |= NETIF_F_SG;

    ret = call_netdevice_notifiers(NETDEV_POST_INIT, dev);
    notifier_to_errno(ret);

    /* 初始化設(shè)備模型實(shí)例堵第,并添加到/sys/devices/virtual/net/。
       如果(!dev->parent && dev->class)崇堵,則在virtual目錄下創(chuàng)建class對應(yīng)的目錄型诚,再
       把設(shè)備加入到該目錄下 */
    netdev_register_kobject(dev);

    dev->reg_state = NETREG_REGISTERED;

    __netdev_update_features(dev);

    set_bit(__LINK_STATE_PRESENT, &dev->state);

    linkwatch_init_dev(dev);

    dev_init_scheduler(dev);
    dev_hold(dev);

    /* 把dev->name_hlist添加到net->dev_name_head,
       把dev->ifindex添加到net->dev_index_head鸳劳,
       把dev->dev_list添加到net->dev_base_head */
    list_netdevice(dev);

    add_device_randomness(dev->dev_addr, dev->addr_len);

    /* If the device has permanent device address, driver should
     * set dev_addr and also addr_assign_type should be set to
     * NET_ADDR_PERM (default value).
     */
    if (dev->addr_assign_type == NET_ADDR_PERM)
        memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);

    /* Notify protocols, that a new device appeared. */
    ret = call_netdevice_notifiers(NETDEV_REGISTER, dev);
    ret = notifier_to_errno(ret);
    if (ret) {
        rollback_registered(dev);
        dev->reg_state = NETREG_UNREGISTERED;
    }
    /*
     *    Prevent userspace races by waiting until the network
     *    device is fully setup before sending notifications.
     */
    if (!dev->rtnl_link_ops ||
        dev->rtnl_link_state == RTNL_LINK_INITIALIZED)
        rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U, GFP_KERNEL);

out:
    return ret;

err_uninit:
    if (dev->netdev_ops->ndo_uninit)
        dev->netdev_ops->ndo_uninit(dev);
    goto out;
}

硬件初始化

static void ag71xx_hw_init(struct ag71xx *ag)
{
    struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
    u32 reset_mask = pdata->reset_bit;

    /* 關(guān)閉DMA中斷與傳輸 */
    ag71xx_hw_stop(ag);

    if (pdata->is_ar724x) {
        u32 reset_phy = reset_mask;

        reset_phy &= AR71XX_RESET_GE0_PHY | AR71XX_RESET_GE1_PHY;
        reset_mask &= ~(AR71XX_RESET_GE0_PHY | AR71XX_RESET_GE1_PHY);
        /* 復(fù)位phy */
        ath79_device_reset_set(reset_phy);
        mdelay(50);
        /* 解復(fù)位phy */
        ath79_device_reset_clear(reset_phy);
        mdelay(200);
    }
    /* 重啟MAC所有模塊 */
    ag71xx_sb(ag, AG71XX_REG_MAC_CFG1, MAC_CFG1_SR);
    udelay(20);
    
    /* 復(fù)位PHY之外的所有模塊 */
    ath79_device_reset_set(reset_mask);
    mdelay(100);
    /* 解復(fù)位PHY之外的所有模塊 */
    ath79_device_reset_clear(reset_mask);
    mdelay(200);

    /* 使能MAC幀接收與轉(zhuǎn)發(fā)功能狰贯、設(shè)置MTU */
    ag71xx_hw_setup(ag);
    /* 配置并使能DMA */
    ag71xx_dma_reset(ag);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市赏廓,隨后出現(xiàn)的幾起案子涵紊,更是在濱河造成了極大的恐慌,老刑警劉巖幔摸,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件摸柄,死亡現(xiàn)場離奇詭異,居然都是意外死亡既忆,警方通過查閱死者的電腦和手機(jī)驱负,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來患雇,“玉大人跃脊,你說我怎么就攤上這事】林ǎ” “怎么了酪术?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長翠储。 經(jīng)常有香客問我绘雁,道長,這世上最難降的妖魔是什么援所? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任庐舟,我火速辦了婚禮,結(jié)果婚禮上住拭,老公的妹妹穿的比我還像新娘继阻。我一直安慰自己耻涛,他們只是感情好废酷,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布瘟檩。 她就那樣靜靜地躺著,像睡著了一般澈蟆。 火紅的嫁衣襯著肌膚如雪墨辛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天趴俘,我揣著相機(jī)與錄音睹簇,去河邊找鬼。 笑死寥闪,一個(gè)胖子當(dāng)著我的面吹牛太惠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播疲憋,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼凿渊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了缚柳?” 一聲冷哼從身側(cè)響起埃脏,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎秋忙,沒想到半個(gè)月后彩掐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡灰追,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年堵幽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弹澎。...
    茶點(diǎn)故事閱讀 40,675評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡朴下,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出裁奇,到底是詐尸還是另有隱情桐猬,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布刽肠,位于F島的核電站溃肪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏音五。R本人自食惡果不足惜惫撰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望躺涝。 院中可真熱鬧厨钻,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至诱建,卻和暖如春蝴蜓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背俺猿。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工茎匠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人押袍。 一個(gè)月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓诵冒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親谊惭。 傳聞我的和親對象是個(gè)殘疾皇子汽馋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容