1. 程式人生 > >Etherlab原始碼解析--ecdev_offer()

Etherlab原始碼解析--ecdev_offer()

在linux系統中,網絡卡及對應的net_device結構體例項是由上層的網路子系統操作的,ecdev_offer()的作用是將網絡卡轉交給ehterlab master操作。

一、預備知識net_device結構體

linux系統中,每一個網絡卡對應一個net_device結構體的例項,一個網絡卡要能夠被核心識別並收發資料,一般需要經過net_device結構體的建立、初始化、註冊到核心、開啟裝置等步驟:
在這裡插入圖片描述

其中,net_device的建立一般在網絡卡驅動程式的probe函式中完成。
net_device是一個很大的結構體,包含很多成員變數和函式指標,因此初始化工作分散在多處完成,與網絡卡硬體細節無關的通用資料域由核心的網路子系統初始化,如傳送佇列長度、MTU等,與網絡卡硬體相關的資料域由網絡卡的驅動程式初始化,如MAC地址、中斷號等。
初始化完成後,net_device將被註冊到核心中,並加入到dev_base_head開始的雙向連結串列中,之後上層的協議(如TCP/IP)就可以通過呼叫net_device中的函式完成資料的收發。
在這裡插入圖片描述


然而,EtherCAT資料包不需要經過TCP/IP協議棧,因此net_device結構體不需要註冊到核心中,而是由ecdev_offer註冊到etherlab master中。

二、預備知識Socket Buffer

linux網路子系統中,每一個要傳送和接收的資料包都對應一個Socket Buffer。
當要傳送資料包時,核心的套接字層會分配一個Socket Buffer,用來儲存將要傳送的資料包。
當網絡卡收到一個數據包時,網絡卡的驅動程式會呼叫dev_alloc_skb()函式分配一個Socket Buffer,用來接收網絡卡中的資料包。

在IGH Etherlab中,在初始化函式ec_device_init()中提前分配了固定個數的Socket Buffer:

    for (i = 0; i < EC_TX_RING_SIZE; i++) {
        if (!(device->tx_skb[i] = dev_alloc_skb(ETH_FRAME_LEN))) {
            EC_MASTER_ERR(master, "Error allocating device socket buffer!\n");
            ret = -ENOMEM;
            goto out_tx_ring;
        }

三、ecdev_offer

以e1000e驅動為例,在drivers\net\ethernet\intel\e1000e\netdev.c中的e1000_probe()函式中呼叫ecdev_offer(),將網絡卡驅動對應的net_device結構體例項註冊到etherlab master中:

static int __devinit e1000_probe(struct pci_dev *pdev,
				 const struct pci_device_id *ent)
{
      ......
      
	adapter->ecdev = ecdev_offer(netdev, ec_poll, THIS_MODULE);
	if (adapter->ecdev) { //如果網絡卡作為ethercat 驅動則在探測後直接開啟
		adapter->ec_watchdog_jiffies = jiffies;
		if (ecdev_open(adapter->ecdev)) {
			ecdev_withdraw(adapter->ecdev);
			goto err_register;
		}
	} else { //如果作為通用驅動則註冊到核心
		strlcpy(netdev->name, "eth%d", sizeof(netdev->name));
		err = register_netdev(netdev);
		if (err)
			goto err_register;

		/* carrier off reporting is important to ethtool even BEFORE open */
		netif_carrier_off(netdev);
	}
	
     ......
 }
ec_device_t *ecdev_offer(
        struct net_device *net_dev, /**< net_device to offer */
        ec_pollfunc_t poll, /**< device poll function */
        struct module *module /**< pointer to the module */
        )
{
        ......
        for (dev_idx = EC_DEVICE_MAIN;
                dev_idx < ec_master_num_devices(master); dev_idx++) {
            if (!master->devices[dev_idx].dev
                && (ec_mac_equal(master->macs[dev_idx], net_dev->dev_addr)
                    || ec_mac_is_broadcast(master->macs[dev_idx]))) {

                EC_INFO("Accepting %s as %s device for master %u.\n",
                        str, ec_device_names[dev_idx != 0], master->index);

                ec_device_attach(&master->devices[dev_idx],
                        net_dev, poll, module);
                up(&master->device_sem);

                snprintf(net_dev->name, IFNAMSIZ, "ec%c%u",
                        ec_device_names[dev_idx != 0][0], master->index);

                return &master->devices[dev_idx]; // offer accepted
            }
        }

        up(&master->device_sem);

        EC_MASTER_DBG(master, 1, "Master declined device %s.\n", str);
    }

    return NULL; // offer declined
}

其中,ecdev_offer()中最重要的是呼叫ec_device_attach()函式:

void ec_device_attach(
        ec_device_t *device, /**< EtherCAT device */
        struct net_device *net_dev, /**< net_device structure */
        ec_pollfunc_t poll, /**< pointer to device's poll function */
        struct module *module /**< the device's module */
        )
{
    unsigned int i;
    struct ethhdr *eth;

    ec_device_detach(device); // resets fields

    device->dev = net_dev;
    device->poll = poll;  //接收EtherCAT資料包所用的函式
    device->module = module;
     
    //初始化傳送佇列中的Socket Buffer, 使其指向網絡卡對應的net_device
    for (i = 0; i < EC_TX_RING_SIZE; i++) {
        device->tx_skb[i]->dev = net_dev;
        eth = (struct ethhdr *) (device->tx_skb[i]->data);
        memcpy(eth->h_source, net_dev->dev_addr, ETH_ALEN);
    }

}

至此,etherlab就可以繞過linux系統中的TCP/IP協議棧,直接通過網絡卡驅動程式所提供的函式收發EtherCAT資料包了。