网卡都是pci设备,因此这里每个网卡驱动其实就是一个pci驱动。并且intel这里是把好几个万兆网卡(82599/82598/x540)的驱动做在一起的。V4L2 一样几个类型摄像头合并在一起

  先说一下 驱动总线平台;实际上就是platform_device(设备)platform_driver(驱动)platform(虚拟总线)上的注册、匹配,相互绑定。 但是最核心的还是cdev file_opterations 等

  • :struct bus_type-->它包含的最关键的函数:match() (要注意的是,这块由内核完成,我们不参与)
  • :struct platform_device-->注册:platform_device_register(unregister)
  • :struct platform_driver-->注册:platform_driver_register(unregister)

设备(或驱动)注册的时候,都会引发总线调用自己的match函数来寻找目前platform总线是否挂载有与该设备(或驱动)名字匹配的驱动(或设备),如果存在则将双方绑定;

platform_device:提供设备的硬件资源 比如 中断号 寄存器地址  DMA

platform_driver : 提供  match id 绑定驱动和device资源的回调probe

详细 就不说了 很简单。。。。 之前在csdn上写过。。后续慢慢找回吧!!!!!!

假设:device先注册, 后续注册driver时,根据platform_match 匹配到了设备;

那么就会调用probe 实现资源的绑定!!!  一般都是  创建设备  注册中断  初始化 file_opteriton 等回调函数

看下 ixgbe_probe 就知道:

  1. static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
  2. {
  3. struct net_device *netdev;
  4. struct ixgbe_adapter *adapter = NULL;
  5. struct ixgbe_hw *hw;
  6. /*
  7. static const struct ixgbe_info *ixgbe_info_tbl[] = {
  8. [board_82598] = &ixgbe_82598_info,
  9. [board_82599] = &ixgbe_82599_info,
  10. [board_X540] = &ixgbe_X540_info,
  11. [board_X550] = &ixgbe_X550_info,
  12. [board_X550EM_x] = &ixgbe_X550EM_x_info,
  13. [board_x550em_a] = &ixgbe_x550em_a_info,
  14. };有很多网卡,根据mactch的记过选择对用的设备信息
  15. */
  16. const struct ixgbe_info *ii = ixgbe_info_tbl[ent->driver_data];

 根据硬件的参数来取得对应的devices info

通过对应的netdev的结构取得adapter,然后所有的核心操作都是保存在adapter中:

  1. netdev = alloc_etherdev_mq(sizeof(struct ixgbe_adapter), indices);
  2. // 分配net_device和ixgbe_adapter,发送队列数为MAX_TX_QUEUE
  3. if (!netdev) {
  4. err = -ENOMEM;
  5. goto err_alloc_etherdev;
  6. }
  7.  
  8. SET_NETDEV_DEV(netdev, &pdev->dev);
  9.  
  10. adapter = netdev_priv(netdev);// 得到ixgbe_adapter的指针
  11.  
  12. adapter->netdev = netdev;
  13. adapter->pdev = pdev;
  14. hw = &adapter->hw;// 得到ixgbe_hw的指针
  15. hw->back = adapter;
  16. adapter->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE);
  17. // 将BAR0中的总线地址映射成内存地址,赋给hw->hw_addr,允许网卡驱动通过hw->hw_addr访问网卡的BAR0对应的Memory空间
  18. hw->hw_addr = ioremap(pci_resource_start(pdev, 0),
  19. pci_resource_len(pdev, 0));
  20. adapter->io_addr = hw->hw_addr;
  1. netdev->netdev_ops = &ixgbe_netdev_ops;// 注册ixgbe_netdev_ops
  2. ixgbe_set_ethtool_ops(netdev);
  3. netdev->watchdog_timeo = 5 * HZ;
  4. strlcpy(netdev->name, pci_name(pdev), sizeof(netdev->name));
  5.  
  6. /* Setup hw api */
  7. hw->mac.ops = *ii->mac_ops;
  8. hw->mac.type = ii->mac;
  9. hw->mvals = ii->mvals;
  10.  
  11. /* EEPROM */
  12. hw->eeprom.ops = *ii->eeprom_ops;
  13. eec = IXGBE_READ_REG(hw, IXGBE_EEC(hw));
  14. if (ixgbe_removed(hw->hw_addr)) {
  15. err = -EIO;
  16. goto err_ioremap;
  17. }
  18. /* If EEPROM is valid (bit 8 = 1), use default otherwise use bit bang */
  19. if (!(eec & BIT(8)))
  20. hw->eeprom.ops.read = &ixgbe_read_eeprom_bit_bang_generic;
  21.  
  22. /* PHY 硬件资源的初始化。。。。。。。。*/
  23. hw->phy.ops = *ii->phy_ops;
  24. hw->phy.sfp_type = ixgbe_sfp_type_unknown;
  25. /* ixgbe_identify_phy_generic will set prtad and mmds properly */
  26. hw->phy.mdio.prtad = MDIO_PRTAD_NONE;
  27. hw->phy.mdio.mmds = 0;
  28. hw->phy.mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22;
  29. hw->phy.mdio.dev = netdev;
  30. hw->phy.mdio.mdio_read = ixgbe_mdio_read;
  31. hw->phy.mdio.mdio_write = ixgbe_mdio_write;
  32.  
  33. ii->get_invariants(hw);

设置网卡属性 ethtool 可以查看

  1. #define IXGBE_GSO_PARTIAL_FEATURES (NETIF_F_GSO_GRE | \
  2. NETIF_F_GSO_GRE_CSUM | \
  3. NETIF_F_GSO_IPXIP4 | \
  4. NETIF_F_GSO_IPXIP6 | \
  5. NETIF_F_GSO_UDP_TUNNEL | \
  6. NETIF_F_GSO_UDP_TUNNEL_CSUM)
  7.  
  8. netdev->gso_partial_features = IXGBE_GSO_PARTIAL_FEATURES;
  9. netdev->features |= NETIF_F_GSO_PARTIAL |
  10. IXGBE_GSO_PARTIAL_FEATURES;
  11.  
  12. if (hw->mac.type >= ixgbe_mac_82599EB)
  13. netdev->features |= NETIF_F_SCTP_CRC;ixgbe_msix_clean_rings
  14.  
  15. /* copy netdev features into list of user selectable features */
  16. netdev->hw_features |= netdev->features |
  17. NETIF_F_HW_VLAN_CTAG_RX |
  18. NETIF_F_HW_VLAN_CTAG_TX |
  19. NETIF_F_RXALL |
  20. NETIF_F_HW_L2FW_DOFFLOAD;
  21.  
  22. if (hw->mac.type >= ixgbe_mac_82599EB)
  23. netdev->hw_features |= NETIF_F_NTUPLE |
  24. NETIF_F_HW_TC;
  25.  
  26. if (pci_using_dac)
  27. netdev->features |= NETIF_F_HIGHDMA;
  28.  
  29. netdev->vlan_features |= netdev->features | NETIF_F_TSO_MANGLEID;
  30. netdev->hw_enc_features |= netdev->vlan_features;

还有注册中断

在enable 设备时 就会ixgbe_request_irq

  1. err = request_irq(entry->vector, &ixgbe_msix_clean_rings, 0,
  2. q_vector->name, q_vector);

    static irqreturn_t ixgbe_msix_clean_rings(int irq, void *data)
    {
        struct ixgbe_q_vector *q_vector = data;

        /* EIAM disabled interrupts (on this vector) for us */

        if (q_vector->rx.ring || q_vector->tx.ring)
            napi_schedule_irqoff(&q_vector->napi);

        return IRQ_HANDLED;
    }
    也就是 注册了napi 回调函数。。。 最后执行其对应的poll

  1. /* initialize NAPI */
  2. netif_napi_add(adapter->netdev, &q_vector->napi,
  3. ixgbe_poll, 64);
    void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
                int (*poll)(struct napi_struct *, int), int weight)
    {
        INIT_LIST_HEAD(&napi->poll_list);
        hrtimer_init(&napi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
        napi->timer.function = napi_watchdog;
        napi->gro_count = 0;
        napi->gro_list = NULL;
        napi->skb = NULL;
        napi->poll = poll;
        if (weight > NAPI_POLL_WEIGHT)
            pr_err_once("netif_napi_add() called with weight %d on device %s\n",
                    weight, dev->name);
        napi->weight = weight;
        list_add(&napi->dev_list, &dev->napi_list);
        napi->dev = dev;
    #ifdef CONFIG_NETPOLL
        spin_lock_init(&napi->poll_lock);
        napi->poll_owner = -1;
    #endif
        set_bit(NAPI_STATE_SCHED, &napi->state);
        napi_hash_add(napi);
    }

注册网络设备dev

  1. strcpy(netdev->name, "eth%d");
  2. err = register_netdev(netdev);----->if (dev->netdev_ops->ndo_init) {ret = dev->netdev_ops->ndo_init(dev)

netdev->netdev_ops = &ixgbe_netdev_ops;

poll:NAPI驱动最终是会调用网卡驱动挂载的poll回调,在ixgbe中,对应的回调就是ixgbe_poll

  1. /**
  2. * ixgbe_poll - NAPI Rx polling callback
  3. * @napi: structure for representing this polling device
  4. * @budget: how many packets driver is allowed to clean
  5. *
  6. * This function is used for legacy and MSI, NAPI mode
  7. **/
  8. int ixgbe_poll(struct napi_struct *napi, int budget)
  9. {
  10. struct ixgbe_q_vector *q_vector =
  11. container_of(napi, struct ixgbe_q_vector, napi);
  12. struct ixgbe_adapter *adapter = q_vector->adapter;
  13. struct ixgbe_ring *ring;
  14. int per_ring_budget, work_done = 0;
  15. bool clean_complete = true;
  16.  
  17. #ifdef CONFIG_IXGBE_DCA
  18. if (adapter->flags & IXGBE_FLAG_DCA_ENABLED)
  19. ixgbe_update_dca(q_vector);
  20. #endif
  21.  
  22. ixgbe_for_each_ring(ring, q_vector->tx) { TX
  23. if (!ixgbe_clean_tx_irq(q_vector, ring, budget))
  24. clean_complete = false;
  25. }
  26.  
  27. /* Exit if we are called by netpoll or busy polling is active */
  28. if ((budget <= 0) || !ixgbe_qv_lock_napi(q_vector))
  29. return budget;
  30.  
  31. /* attempt to distribute budget to each queue fairly, but don't allow
  32. * the budget to go below 1 because we'll exit polling */
  33. if (q_vector->rx.count > 1)
  34. per_ring_budget = max(budget/q_vector->rx.count, 1);
  35. else
  36. per_ring_budget = budget;
  37.  
  38. ixgbe_for_each_ring(ring, q_vector->rx) {RX
  39. int cleaned = ixgbe_clean_rx_irq(q_vector, ring,
  40. per_ring_budget);
  41.  
  42. work_done += cleaned;
  43. if (cleaned >= per_ring_budget)
  44. clean_complete = false;
  45. }
  46.  
  47. ixgbe_qv_unlock_napi(q_vector);
  48. /* If all work not completed, return budget and keep polling */
  49. if (!clean_complete)
  50. return budget;
  51.  
  52. /* all work done, exit the polling mode */
  53. napi_complete_done(napi, work_done);
  54. if (adapter->rx_itr_setting & 1)
  55. ixgbe_set_itr(q_vector);
  56. if (!test_bit(__IXGBE_DOWN, &adapter->state))
  57. ixgbe_irq_enable_queues(adapter, BIT_ULL(q_vector->v_idx));
  58.  
  59. return min(work_done, budget - 1);
  60. }

ndo_start_xmit---->ixgbe_xmit_frame,这个回调就是驱动提供给协议栈的发送回调接口。我们来看这个函数.

它的实现很简单,就是选取对应的队列,然后调用ixgbe_xmit_frame_ring来发送数据。

  1. static netdev_tx_t __ixgbe_xmit_frame(struct sk_buff *skb,
  2. struct net_device *netdev,
  3. struct ixgbe_ring *ring)
  4. {
  5. struct ixgbe_adapter *adapter = netdev_priv(netdev);
  6. struct ixgbe_ring *tx_ring;
  7.  
  8. /*
  9. * The minimum packet size for olinfo paylen is 17 so pad the skb
  10. * in order to meet this minimum size requirement.
  11. */
  12. if (skb_put_padto(skb, 17))
  13. return NETDEV_TX_OK;
  14.  
  15. tx_ring = ring ? ring : adapter->tx_ring[skb->queue_mapping];
  16.  
  17. return ixgbe_xmit_frame_ring(skb, adapter, tx_ring);
  18. }

用ixgbe_tx_map来发送数据。而ixgbe_tx_map所做的最主要是两步,第一步请求DMA,第二步写寄存器,通知网卡发送数据.

  1. netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb,
  2. struct ixgbe_adapter *adapter,
  3. struct ixgbe_ring *tx_ring)
  4. {
  5. struct ixgbe_tx_buffer *first;
  6. int tso;
  7. u32 tx_flags = 0;
  8. unsigned short f;
  9. u16 count = TXD_USE_COUNT(skb_headlen(skb));
  10. __be16 protocol = skb->protocol;
  11. u8 hdr_len = 0;
  12.  
  13. /*
  14. * need: 1 descriptor per page * PAGE_SIZE/IXGBE_MAX_DATA_PER_TXD,
  15. * + 1 desc for skb_headlen/IXGBE_MAX_DATA_PER_TXD,
  16. * + 2 desc gap to keep tail from touching head,
  17. * + 1 desc for context descriptor,
  18. * otherwise try next time
  19. */
  20. for (f = 0; f < skb_shinfo(skb)->nr_frags; f++)
  21. count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size);
  22.  
  23. if (ixgbe_maybe_stop_tx(tx_ring, count + 3)) {
  24. tx_ring->tx_stats.tx_busy++;
  25. return NETDEV_TX_BUSY;
  26. }
  27.  
  28. /* record the location of the first descriptor for this packet */
  29. first = &tx_ring->tx_buffer_info[tx_ring->next_to_use];
  30. first->skb = skb;
  31. first->bytecount = skb->len;
  32. first->gso_segs = 1;
  33.  
  34. -------------------------------------
  35. ixgbe_tx_map(tx_ring, first, hdr_len);
  36.  
  37. return NETDEV_TX_OK;
  38.  
  39. out_drop:
  40. dev_kfree_skb_any(first->skb);
  41. first->skb = NULL;
  42.  
  43. return NETDEV_TX_OK;
  44. }

上面的操作是异步的,此时内核还不能释放SKB,而是网卡硬件发送完数据之后,会再次产生中断通知内核,然后内核才能释放内存 ; 此时可以看到 napi的poll 回调函数  会清除tx 队列

ixgbe 驱动 为xxx驱动做准备1的更多相关文章

  1. Windows网络驱动、NDIS驱动(微端口驱动、中间层驱动、协议驱动)、TDI驱动(网络传输层过滤)、WFP(Windows Filtering Platform)

    catalog . 引言 . Windows 2000网络结构和OSI模型 . NDIS驱动 . NDIS微端口驱动编程实例 . NDIS中间层驱动编程实例 . NDIS协议层驱动编程实例 . TDI ...

  2. spi驱动框架全面分析,从master驱动到设备驱动

    内核版本:linux2.6.32.2  硬件资源:s3c2440 参考:  韦东山SPI视频教程 内容概括:     1.I2C 驱动框架回顾     2.SPI 框架简单介绍     3.maste ...

  3. WDM驱动和NT驱动之我见

    WDM驱动是NT驱动的进化版.我个人觉得它的主要好处有两个 1.能检测到设备的插入,系统能自动分配设备的硬件信息,如中断号.IO端口.设备物理地址等 2.支持设备的开机状态拔出 之前的NT驱动和硬件关 ...

  4. 字符设备驱动、平台设备驱动、设备驱动模型、sysfs的比较和关联

    转载自:http://www.kancloud.cn/yueqian_scut/emlinux/106829 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sy ...

  5. 乾坤合一~Linux设备驱动之终端设备驱动

    多想拥你在我的怀里 却无法超越那距离 美好回忆渐渐地远去 盼望今生出现奇迹 无尽的想念 荒了容颜 无助的爱恋 从未改变 这是今天的旋律,,,,今生今世,遥不可及~ 1 终端设备 终端是一种字符型设备, ...

  6. linux 驱动之LCD驱动(有framebuffer)

    <简介> LCD驱动里有个很重要的概念叫帧缓冲(framebuffer),它是Linux系统为显示设备提供的一个接口,应用程序在图形模式允许对显示缓冲区进行读写操作.用户根本不用关心物理显 ...

  7. [kernel]字符设备驱动、平台设备驱动、设备驱动模型、sysfs几者之间的比较和关联

    转自:http://www.2cto.com/kf/201510/444943.html Linux驱动开发经验总结,绝对干货! 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动 ...

  8. 二十、网卡框架分析、虚拟网卡驱动和DM9621驱动分析

    一.网络设备驱动的结构 网卡设备不同于字符设备和块设备, 网络设备并不对应于/dev目录下的文件,它存放在/sys/class/net目录下. Linux系统对网络设备驱动定义了四个层次: 1. 网络 ...

  9. (55)Linux驱动开发之一驱动概述

                                                                                                      驱动 ...

随机推荐

  1. 本文介绍如何使用 Docker Swarm 来部署 Nebula Graph 集群,并部署客户端负载均衡和高可用

    本文作者系:视野金服工程师 | 吴海胜 首发于 Nebula Graph 论坛:https://discuss.nebula-graph.com.cn/t/topic/1388 一.前言 本文介绍如何 ...

  2. [论文阅读]阿里DIN深度兴趣网络之总体解读

    [论文阅读]阿里DIN深度兴趣网络之总体解读 目录 [论文阅读]阿里DIN深度兴趣网络之总体解读 0x00 摘要 0x01 论文概要 1.1 概括 1.2 文章信息 1.3 核心观点 1.4 名词解释 ...

  3. 如何把C++的源代码改写成C代码?而C改C++只需一步!

    ★ 如何把C++的源代码改写成C代码? C++解释器比C语言解释器占用的存储空间要大,想要在某些特定场合兼容C++代码,同时为了节省有限的存储空间,降低成本,也为了提高效率,将用C++语言写的源程序用 ...

  4. linux(centos8):安装分布式事务服务seata(file单机模式,seata 1.3.0/centos 8.2)

    一,什么是seata? Seata:Simpe Extensible Autonomous Transcaction Architecture, 是阿里中间件,开源的分布式事务解决方案. 前身是阿里的 ...

  5. java安全编码指南之:ThreadPool的使用

    目录 简介 java自带的线程池 提交给线程池的线程要是可以被中断的 正确处理线程池中线程的异常 线程池中使用ThreadLocal一定要注意清理 简介 在java中,除了单个使用Thread之外,我 ...

  6. 使用经纬度得到位置Geocorder

    先得到经纬度再用geocorder 显示位置,需要手机打开位置权限,使用GPS的话把注释去掉,GPS在室内很容易收不到信号,得到位置为空 public class MainActivity exten ...

  7. Redis的一些问题

    date: 2020-10-15 10:58:00 updated: 2020-10-19 18:00:00 Redis的一些问题 Remote Dictionary Server 底层C写的 类似于 ...

  8. Vue 路由切换时页面内容刷新页面并更新数据

    第二次进入页面,页面路由参数已经改变,但是页面内容不会刷新 <keep-alive>是Vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM <keep-aliv ...

  9. Navicat连接Mysql8.0.17出现1251错误 / 或者Navicat Premium出现2059错误

    Navicat连接Mysql8.0.17出现1251错误 重装了电脑之后,好多软件出了问题,经过一系列的插件安装,mysql终于安装好了 但是Navicat又抽筋了~~~额(⊙o⊙)... 在网上查的 ...

  10. JS删除微博

    昨天晚上找回了10年注册的微博,现在瞅瞅,转发过很多傻吊的微博,关注了一堆营销号,不忍直视,动手删吧!开玩笑的,怎么可能手动! 查看自己的所有微博,F12----->console,负责下面代码 ...