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数据包了。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值