美文网首页
kernel 网络驱动

kernel 网络驱动

作者: henry_zeng | 来源:发表于2016-09-10 23:10 被阅读0次

    网络设备#

    网络驱动结构##

    从上到下划分4层:

    • 网络协议接口层

    使上层协议独立于具体设备

    • 网络设备接口层

    向协议接口层提供统一的用于描述具体网络设别属性和操作的结构体 net_device

    • 设备驱动功能层(提供实际功能)

    net_device 的具体成员

    • 网络设备与媒介层

    完成数据包发送和接收的物理实体

    网络设备初始化##

    • 硬件准备工作,检查设备是否存在和硬件资源
    • net_device 数据和函数指针初始化
    • 获得设备的私有信息指针并初始化各成员值

    网络设备打开与释放###

    • open

    • 使能设备硬件资源,申请IO区域、中断和DMA通道

    • 调用Linux内核提供的 netif_start_queue() 函数,激活设备发送队列

    • release

    • 调用内核提供的 netif_stop_queue() 函数

    • 释放资源

    数据发送流程###

    • 获取sk_buff数据包的数据和长度
    • 有效长度小于以太网冲突检测所要求数据帧最小长度ETH_ZLEN,则补0
    • 设置硬件寄存器,驱动设备进行数据发送

    数据接收流程###

    • 设备接收数据的主要方法是由中断引发中断处理函数
    • NAPI兼容的驱动,需先disable interrupt再调用NAPI接收pkt

    网络状态###

    • 网络硬件电路可以检测网络连接是否正常
    • 驱动程序可采取一定手段检测报告链路状态,中断或定时

    网络设备驱动流程##

    
    struct net_device_stats(struct net_device *dev)
    {
        ...
        return &dev->stats;
    }
    
    static int xxx_config(struct net_device *dev, struct ifmap *map)
    {
        if (netif_running(dev))
            return -EBUSY;
    
        /* 假设不允许改变IO地址 */
        if (map->base_addr != dev->base_addr) {
            printk("xxx: Can't change I/O address\n");
            return -EOPNOTSUPP;
        }
    
        /* 假设允许改变 IRQ */
        if (map->irq != dev->irq)
            dev->irq = map->irq;
    
        return 0;
    }
    
    static int set_mac_address(struct net_device *dev, void *addr)
    {
        if (netif_running(dev))
            return -EBUSY;
    
        xxx_set_mac(dev, addr);
    
        return 0;
    }
    
    static int xxx_open(struct net_device *dev)
    {
        struct xxx_priv *priv = netdev_priv(dev);
    
        ...
        priv->timer.expires = jiffies + 3*Hz;
        priv->timer.data = (unsigned long)dev;
        /* timer handler */
        priv->timer.function = &xxx_timer;
        add_timer(&priv->timer);
        ...
    }
    
    static void xxx_timer(unsigned long data)
    {
        struct net_device *dev = (struct net_device *)data;
        u16 link;
    
        ...
        if (!(dev->flags & IFF_UP))
            goto set_timer;
    
        /* 获得物理连接状态 */
        if (link = xxx_chk_link(dev)) {
            if (!(dev->flags & IFF_RUNNING)) {
                /* 通知内核 link up */
                netif_carrier_on(dev);
                dev->flags |= IFF_RUNNING;
                printk("%s: link up\n", dev->name);
            }
        } else {
            if (dev->flags & IFF_RUNNING) {
                /* 通知内核 link down */
                netif_carrier_off(dev);
                dev->flags &= ~IFF_RUNNING;
                printk("%s: link down\n", dev->name);
            }
        }
    
    set_timer:
        priv->timer.expires = jiffies + 1*Hz;
        priv->timer.data = (unsigned long)dev;
        /* timer handler */
        priv->timer.function = &xxx_timer;
        add_timer(&priv->timer);
    }
    
    #ifdef NAPI
    
    static int xxx_poll(struct napi_struct *napi, int budget)
    {
        int npackets = 0;
        struct sk_buff *skb;
        struct xxx_priv *priv = container_of(napi, struct xxx_priv, napi);
        struct xxx_packet *pkt;
    
        while ((npackets < budget) && priv->rx_queue) {
            /* 从队列中取出数据包 */
            pkt = xxx_dequeue_buf(dev);
    
            /* 接下来的处理和中断触发接收一致 */
            skb = dev_alloc_skb(length + 2);
            ...
            skb_reserve(skb, 2);
            memcpy(skb_put(skb, pkt->datalen), pkt->data, pkt->datalen);
            skb->dev = dev;
            skb->protocol = eth_type_trans(skb, dev);
    
            /* 调用 netif_receive_skb, 而不是net_rx, 将数据包交给上层协议 */
            netif_receive_skb(skb);
    
            /* 更改统计数据 */
            priv->stats.rx_packets++;
            priv->stats.rx_bytes += pkt->datalen;
            xxx_release_buffer(pkt);
            npackets++;
        }
        if (npackets < budget) {
            napi_complete(napi);
            /* 再次启动网络设备的接收中断 */
            xxx_enable_rx_int(...);
        }
        
        return npackets;
    }
    
    static void xxx_interrupt(int irq, void *dev_id)
    {
        struct net_device *dev = dev_id;
    
        ...
        switch (status & ISQ_EVENT_MASK) {
        case ISQ_RECEIVER_EVENT:
            /* 禁止接收中断 */
            xxx_disable_rx_int(...);
    
            /* 获取数据包 */
            napi_schedule(&priv->napi);
            break;
    
        case ISQ_TRANSMITTER_EVENT:
            dev->stats.tx_packets++;
            netif_wake_queue(dev);
    
            /* 检查硬件错误标志 */
            if ((status & (TX_OK | TX_LOST_CRS | TX_SQE_ERROR | TX_LATE_COL | TX_16_COL)) != TX_OK) {
    
                /* 错误统计 */
                if ((status & TX_OK) == 0)
                    dev->stats.tx_errors++;
    
                if (status & TX_LOST_CRS)
                    dev->stats.tx_carrier_errors++;
                
                ...
            }
            break;
    
        case ISQ_RX_MISS_EVENT:
            ...
            break;
    
        ...
        }
    }
    
    #else
    
    static void xxx_rx(struct xxx_device *dev)
    {
        ...
        length = get_rev_len(...);
        skb = dev_alloc_skb(length + 2);
    
        /* 对齐 */
        skb_reserve(skb, 2);
        skb->dev = dev;
    
        /* 读取硬件上接收到的数据 */
        insw(ioaddr + RX_FRAME_PORT, skb_put(skb, length),  length >> 1);
        if (length & 1)
            skb->data[length - 1] = inw(ioaddr + RX_FRAME_PORT);
        
        /* 获取上层协议类型 */
        skb->protocol = eth_type_trans(skb, dev);
    
        /* 把数据包交给上层 */
        netif_rx(skb);
    
        /* 记录接收时间戳 */
        dev->last_rx = jiffies;
        ...
    }
    
    static void xxx_interrupt(int irq, void *dev_id)
    {
        ...
        switch (status & ISQ_EVENT_MASK) {
        case ISQ_RECEIVER_EVENT:
            /* 获取数据包 */
            xxx_rx(dev);
            break;
        ...
        }
    }
    
    #endif
    
    /* 发送超时处理函数 */
    void xxx_tx_timeout(struct net_device *dev)
    {
        ...
        netif_wake_queue(dev);
        ...
        dev->stats.tx_errors++;
    }
    
    int xxx_tx(struct sk_buff *skb, struct net_device *dev)
    {
        int len;
        char *data, shortpkt[ETH_ZLEN];
    
        /* 发送队列未满,可以发送 */
        if (xxx_send_available(...)) {
    
            /* 有效数据指针和长度 */
            data = skb->data;
            len = skb->len;
    
            if (len < ETH_ZLEN) {
                /* 如果帧长小于以太网帧最小长度,补0 */
                memset(shortpkt, 0, ETH_ZLEN);
                memcpy(shortpkt, skb->data, skb->len);
                len = ETH_ZLEN;
                data = shortpkt;
            }
    
            /* 记录发送时间戳 */
            dev->trans_start = jiffies;
    
            if (avail) {
                xxx_hw_tx(data, len, dev);
            } else {
                /* 
                    阻止上层继续向网络设备传递数据包;当忙于发送的数据包被发送完成后,在以TX结束的中断处理中,应该调用 netif_wake_queue() 唤醒被阻塞的上层,以启动它继续向网络设备传送数据包
                */
                netif_stop_queue(dev);
                ...
            }
        }
    }
    
    static int xxx_open(struct net_device *dev)
    {
        /* 申请端口、IRQ等,类似于fops->open */
        ret = request_irq(dev->irq, &xxx_interrupt, 0, dev->name, dev);
        ...
        netif_start_queue(dev);
        ...
    }
    
    static int xxx_release(struct net_device *dev)
    {
        netif_stop_queue(dev);
        ...
        free_irq(dev->irq, dev);
        ...
    }
    
    void xxx_init(struct net_device *dev)
    {
        struct xxx_priv *priv;
    
        /* 检查设备是否存在和设备所使用的硬件资源 */
        xxx_hw_init();
    
        /* 初始化 net_device 公有成员 */
        ether_setup(dev);
    
        /* 设置设备的成员函数指针 */
        dev->netdev_ops = &xxx_netdev_ops;
        dev->ethtool_ops = &xxx_ethtool_ops;
        dev->watchdog_timeo = timeout;
    
        /* 取得私有指针并初始化 */
        priv = netdev_priv(dev);
        ...
    }
    
    static int xxx_register(void)
    {
        ...
        /* 分配 net_device 结构体并赋值 */
        xxx_dev = alloc_netdev(sizeof(struct xxx_priv), "sn%d", xxx_init);
        if (!xxx_dev)
            ...
    
        /* 注册 net_device 结构体 */
        if ((result = register_netdev(xxx_dev)))
            ...
    }
    
    static void xxx_unregister(void)
    {
        ...
        /* 注销 net_device 结构体 */
        unregister_netdev(xxx_dev);
        /* 释放 net_device 结构体 */
        free_netdev(xxx_dev);
    }
    
    

    总结##

    • net_device 结构体的存在将网络设备进行抽象,使得设备功能层中除数据包接收以外的主体工作都由填充 net_device 的属性和函数指针完成
    • 套接字缓冲区 sk_buff 是所有数据流动的载体,网络设备驱动和上层协议之间也基于此结构进行数据包交互

    相关文章

      网友评论

          本文标题:kernel 网络驱动

          本文链接:https://www.haomeiwen.com/subject/ylquettx.html