linux驱动_sigmastar_SSD202D网络驱动简析

本文有部分资料和图片摘录自以下链接(侵删):
https://www.cnblogs.com/xiaojiang1025/archive/2017/03/28/6486267.html
https://blog.51cto.com/liucw/1221140
https://blog.csdn.net/Rong_Toa/article/details/109401935

基础

Linux的网络设备并不使用文件作为用户程序访问网络设备的接口,所以/sys/dev下和/dev下并没有相应的网络设备文件,在Linux中,用户程序最终使用套接字来访问网络设备。Linux的网卡驱动程序处于OSI模型中的数据链路层,他的职责就是将上上层的协议栈传过来的信息通过网卡发送出去。
linux网络体系分层图

sk_buff

网络驱动框架中信息的载体, 是网络分层模型中对数据进行层层打包以及层层解包的载体。网络协议栈中各层协议都可以通过对该结构的操作实现本层协议数据的添加或者删除。使用sk_buff结构避免了网络协议栈各层来回复制数据导致的效率低下。
网络驱动_sk_buff结构体
网络驱动_sk_buff结构体2

net_device

net_device 结构位于网络驱动程序层的最核心地位,描述了一个网络设备, 其中的 *struct net_device_ops netdev_ops 是操作方法集, 向上提供接口的同时也向下操作硬件。

核心API

  • dev_queue_xmit()是网络协议接口层向下发送数据的接口, 内核已经实现, 不需要网络设备驱动实现
  • ndo_start_xmit()是网络设备接口层向下发送数据的接口, 位于net_device->net_device_ops, 会被dev_queue_xmit()回调, 需要网络驱动实现
  • netif_rx()是网络设备接口层向上发送数据的接口, 不需要网络驱动实现
  • 中断处理函数是网络设备媒介层收到数据后向上发送数据的入口, 需要网络驱动实现,最后要调用netif_rx()

数据接收

驱动接收到数据包后调用 netif_rx()函数,把网卡接收到数据提交给协议栈处理
网络驱动_数据包接收流程
注意:
本项目网络驱动使用了NAPI接收数据,接收流程如下:
网络驱动_数据包接收流程_NAPI

数据发送

网络驱动_数据包发送流程

网关网络驱动源码解析

CPU默认有两个以太网卡,设备树代码如下:

emac0: emac0 {
            compatible = "sstar-emac";
            interrupts = <GIC_SPI INT_IRQ_EMAC IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI INT_FIQ_LAN_ESD IRQ_TYPE_LEVEL_HIGH>;
            clocks = <&CLK_emac_ahb>,<&CLK_emac_tx>,<&CLK_emac_rx>;
            reg = <0x1F2A2000 0x800>, <0x1F343C00 0x600>, <0x1F006200 0x600>;
            pad = <0x1F203C38 0x0001 0x0000>; // pad selection from 0x0001
            phy-handle = <&phy0>;
            status = "ok";
            mdio-bus@emac0 {
                phy0: ethernet-phy@0 {
                    phy-mode = "mii";
                };
            };
        };

emac1: emac1 {
            compatible = "sstar-emac";
            interrupts = <GIC_SPI INT_IRQ_EMAC_1 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI INT_FIQ_LAN_ESD IRQ_TYPE_LEVEL_HIGH>;
            clocks = <&CLK_emac_ahb>,<&CLK_emac1_tx>,<&CLK_emac1_rx>,<&CLK_emac1_tx_ref>,<&CLK_emac1_rx_ref>;
            reg = <0x1F2A2800 0x800>, <0x1F344200 0x600>, <0x00000000 0x000>;
            //pad = <0x1F203C38 0x0F00 0x0300>; // pad selection from 0x0100/0x0200/0x0300/0x0400/0x0500/0x0600/0x0700/0x0800/0x0900
            pad = <0x1F203C38 0x0F00 0x0300>; // pad selection from 0x0100/0x0200/0x0300/0x0400/0x0500/0x0600/0x0700/0x0800/0x0900
            status = "ok";
            phy-handle = <&phy1>;
            mdio-bus@emac1 {
                phy1: ethernet-phy@1 {
                    phy-mode = "rmii";
                };
            };
        };

因为要从设备树获取设备参数,emac设备挂载在平台下,所以要先注册了个平台驱动,精简代码如下,初始化代码执行顺序是:
mstar_emac_drv_init_module -> platform_driver_register -> mstar_emac_drv_probe -> MDev_EMAC_init
下面代码有较多的注释,而且精简了许多不便于理解驱动流程的代码,例如操作实际硬件的代码。

// net_device 回调函数
static const struct net_device_ops mstar_lan_netdev_ops = {
        .ndo_init               = MDev_EMAC_ndo_init,
        .ndo_uninit             = MDev_EMAC_ndo_uninit,
        // .ndo_tx_timeout         = MDev_EMAC_ndo_tx_timeout,
        .ndo_change_mtu         = MDev_EMAC_ndo_change_mtu,
        .ndo_open               = MDev_EMAC_open,
        .ndo_stop               = MDev_EMAC_close,
        .ndo_start_xmit         = MDev_EMAC_tx, // 网络接口数据包发送函数
        .ndo_set_mac_address    = MDev_EMAC_set_mac_address,
        .ndo_set_rx_mode        = MDev_EMAC_set_rx_mode,
        .ndo_do_ioctl           = MDev_EMAC_ioctl,
        .ndo_get_stats          = MDev_EMAC_stats,
	.ndo_set_features = MDev_EMAC_set_features,
	.ndo_fix_features = MDev_EMAC_fix_features,
#ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller    = MDev_EMAC_netpoll,
#endif
};

// 获取网络报文并分析,最后上送协议栈
static int MDev_EMAC_rx(struct net_device *dev, int budget)
{
    struct emac_handle *hemac = (struct emac_handle*) netdev_priv(dev);
    unsigned char *p_recv;
    u32 pktlen;
    u32 received=0;
    struct sk_buff *skb;
    struct sk_buff *clean_skb;
    rx_desc_queue_t* rxinfo = &(hemac->rx_desc_queue);

    do
    {
        char* pData;
        struct rbf_t* desc = &(rxinfo->desc[rxinfo->idx]);
        if (!(desc->addr & EMAC_DESC_DONE))
            break;

        p_recv = BUS2VIRT(CLR_BITS(desc->addr, EMAC_DESC_DONE | EMAC_DESC_WRAP));
        pktlen = desc->size & 0x7ffUL;    /* Length of frame including FCS */
        if (unlikely(((pktlen > EMAC_MTU) || (pktlen < 64)))) // 非法报文
        {
            desc->addr = CLR_BITS(desc->addr, EMAC_DESC_DONE);
            Chip_Flush_MIU_Pipe();
            rxinfo->idx++;
            continue;
        }

        if (unlikely(!(clean_skb = alloc_skb (EMAC_PACKET_SIZE_MAX, GFP_ATOMIC))))
            goto jmp_rx_exit;

        skb = rxinfo->skb_arr[rxinfo->idx];
        {
            pktlen -= 4; /* Remove FCS */
            pData = skb_put(skb, pktlen);
            dma_unmap_single(NULL, VIRT2PA(pData), pktlen, DMA_FROM_DEVICE);
        }

        skb->dev = dev;
        skb->protocol = eth_type_trans (skb, dev);
        if (0 == MHal_EMAC_FlowControl_TX(hemac))
            _MDrv_EMAC_Pause_TX(dev, skb, p_recv);

        napi_gro_receive(&hemac->napi, skb); // 转给协议栈处理
        received++;

        //fill clean_skb into RX descriptor
        rxinfo->skb_arr[rxinfo->idx] = clean_skb;

        Chip_Flush_MIU_Pipe();

        rxinfo->idx++;
        if (rxinfo->idx >= rxinfo->num_desc)
            rxinfo->idx = 0;

        if(received >= budget) 
            break;

    } while(1);

jmp_rx_exit:
    if(received>max_rx_packet_count)max_rx_packet_count=received;
    rx_packet_cnt += received;
    return received;
}

// 报文接收
/*
NAPI (New API) 是Linux内核针对网络数据传输做出的一个优化措施。 其目的是在大量数据传输时, 在收到硬件中断后,通过poll方式将传输过来的数据包统一处理, 通过禁止网络设备中断以减少硬件中断数量 ((Interrupt Mitigation),从而实现更高的数据传输。
*/
static int MDev_EMAC_napi_poll(struct napi_struct *napi, int budget)
{
    struct emac_handle  *hemac = container_of(napi, struct emac_handle,napi);
    struct net_device *dev = hemac->netdev;
    int work_done = 0;
    int budget_rmn = budget;
    unsigned long elapse = 0;
    unsigned long packets = 0;

    work_done = MDev_EMAC_rx(dev, budget_rmn); // 接收报文处理

    // 统计计数 。。。 

    napi_gro_flush(napi, false);

    /* If budget not fully consumed, exit the polling mode */
    if (work_done < budget) {
        napi_complete(napi);
        MHal_EMAC_IntEnable(hemac->hal, EMAC_INT_RCOM, 1);
    }
    return work_done;
}

// 网卡中断处理函数
irqreturn_t MDev_EMAC_interrupt(int irq,void *dev_id)
{
    struct net_device *dev = (struct net_device *) dev_id;
    struct emac_handle *hemac = (struct emac_handle*) netdev_priv(dev);
    u32 intstatus=0;

    hemac->irqcnt++;
    hemac->oldTime = getCurMs();
    _MDev_EMAC_tx_pump(hemac, 1, 0); // 发送报文
    while ((intstatus = MHal_EMAC_IntStatus(hemac->hal)))
    {
        _MDrv_EMAC_ISR_RBNA(dev, intstatus);
        _MDrv_EMAC_ISR_TCOM(dev, intstatus);
        _MDrv_EMAC_ISR_DONE(dev, intstatus);
        _MDrv_EMAC_ISR_ROVR(dev, intstatus);
        _MDrv_EMAC_ISR_RCOM(dev, intstatus);
    }

    if (netif_queue_stopped (dev) && skb_queue_free(&hemac->skb_queue_tx, 0))
    {
        MHal_EMAC_IntEnable(hemac->hal, EMAC_INT_TCOM, 0);
        if (0 == timer_pending(&hemac->timerFlowTX))
            netif_wake_queue(dev);
    }

    return IRQ_HANDLED;
}

// 发送函数
static int _MDev_EMAC_tx_pump(struct emac_handle *hemac, int bFree, int bPump)
{

    spin_lock_irqsave(&hemac->mutexTXQ, flags);
    if (bFree)
    {
        txUsedCnt = MHal_EMAC_TXQ_Used(hemac->hal);
        txUsedCntSW = skb_queue_used(&hemac->skb_queue_tx, 0);
        ret = txUsedCntSW - txUsedCnt;
        for (i = txUsedCnt; i < txUsedCntSW; i++)
        {
            len = skb_queue_remove(&hemac->skb_queue_tx, NULL, NULL, 1, 0);
            hemac->stats.tx_bytes += len;
            tx_bytes_per_timer += len;
        }
    }

    if (bPump)
    {
        int skb_len;
        txFreeCnt = skb_queue_free(&hemac->skb_queue_tx, 0);
        txPendCnt = skb_queue_used(&hemac->skb_queue_tx, 2);
        nPkt = (txFreeCnt < txPendCnt) ? txFreeCnt : txPendCnt;
        for (i = 0; i < nPkt; i++)
        {
            skb_queue_head_inc(&hemac->skb_queue_tx, &skb, &skb_addr, &skb_len, 0);
            if (skb_addr)
                MHal_EMAC_TXQ_Insert(hemac->hal, skb_addr, skb_len); // 硬件发送队列
        }
    }
    spin_unlock_irqrestore(&hemac->mutexTXQ, flags);
    return ret;
}

// 发送函数
static int MDev_EMAC_tx(struct sk_buff *skb, struct net_device *dev)
{
    struct emac_handle *hemac = (struct emac_handle*) netdev_priv(dev);
    dma_addr_t skb_addr;
    int ret = NETDEV_TX_OK;

    if (skb_queue_full(&hemac->skb_queue_tx, 1)) // 发送队列满
    {
        netif_stop_queue(dev);
        MHal_EMAC_IntEnable(hemac->hal, EMAC_INT_TCOM, 1);
        ret = NETDEV_TX_BUSY;
        goto out_unlock;
    }

    {
        int i;
        int nr_frags = skb_shinfo(skb)->nr_frags;
        int len;

        // dma_unmap_single(NULL, VIRT2PA(start), EMAC_MTU, DMA_TO_DEVICE);
        if (nr_frags)
        {
            char* start = kmalloc(EMAC_MTU, GFP_ATOMIC);
            char* p = start;

            if (!start)
            {
                ret = NETDEV_TX_BUSY;
                goto out_unlock;
            }

            memcpy(p, skb->data, skb_headlen(skb));
            p += skb_headlen(skb);
            len = skb_headlen(skb);
            for (i = 0; i < nr_frags; i++)
            {
                struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i];
                memcpy(p, skb_frag_address(frag), skb_frag_size(frag));
                p += skb_frag_size(frag);
                len += skb_frag_size(frag);
            }

            if (EMAC_SG_BUF_CACHE)
                Chip_Flush_Cache_Range((size_t)start, skb->len);
            skb_addr = VIRT2BUS(start);
            spin_lock_irqsave(&hemac->mutexTXQ, flags);
            skb_queue_insert(&(hemac->skb_queue_tx), (struct sk_buff*)0xFFFFFFFF, (dma_addr_t)skb_addr, skb->len, 1);
            spin_unlock_irqrestore(&hemac->mutexTXQ, flags);
            // Chip_Flush_Cache_Range((size_t)start, skb->len);
            dev_kfree_skb_any(skb);
        }
        else
        {
            {
                struct sk_buff* skb_tmp = skb_clone(skb, GFP_ATOMIC);
                if (!skb_tmp)
                {
                    printk("[%s][%d] skb_clone fail\n", __FUNCTION__, __LINE__);
                    ret = NETDEV_TX_BUSY;
                    goto out_unlock;
                }
                dev_kfree_skb_any(skb);
                skb = skb_tmp;
            }
            skb_addr = VIRT2BUS(skb->data);
            spin_lock_irqsave(&hemac->mutexTXQ, flags);
            skb_queue_insert(&(hemac->skb_queue_tx), skb, skb_addr, skb->len, 1);
            spin_unlock_irqrestore(&hemac->mutexTXQ, flags);
            Chip_Flush_Cache_Range((size_t)skb->data, skb->len);
        }
    }

    netif_trans_update(dev);
out_unlock:
    _MDev_EMAC_tx_pump(hemac, 1, 1); // 发送报文
    return ret;
}

static int MDev_EMAC_setup (struct net_device *dev)
{
    struct emac_handle *hemac;

    hemac = (struct emac_handle *) netdev_priv(dev);

    hemac->netdev = dev;

    RetAddr = MDev_EMAC_VarInit(hemac); // 初始化hemac变量,包括获取mac地址

    skb_queue_create(&(hemac->skb_queue_tx), MHal_EMAC_TXQ_Size(hemac->hal), MHal_EMAC_TXQ_Size(hemac->hal)+ hemac->txq_num_sw);

    MDev_EMAC_HW_init(dev); // 初始化 mac 层寄存器,包括设置 mac 地址,申请 sk_buff 队列

    spin_lock_init(&hemac->mutexNetIf);
    spin_lock_init(&hemac->mutexPhy);
    spin_lock_init(&hemac->mutexTXQ);

    ether_setup (dev);
    dev->tx_queue_len = EMAC_MAX_TX_QUEUE;

    spin_lock_irqsave(&hemac->mutexPhy, flags);
    MDev_EMAC_get_mac_address (dev);    // Get ethernet address and store it in dev->dev_addr //
    MDev_EMAC_update_mac_address (dev); // Program ethernet address into MAC //
    MHal_EMAC_enable_mdi(hemac->hal);
    spin_unlock_irqrestore(&hemac->mutexPhy, flags);

    dev->features |= EMAC_FEATURES;
    dev->vlan_features |= EMAC_FEATURES;
    hemac->irqcnt=0;
    hemac->tx_irqcnt=0;
    dev->irq = hemac->irq_emac;

    // 申请中断
    if (request_irq(dev->irq, MDev_EMAC_interrupt, 0/*SA_INTERRUPT*/, dev->name, dev))
        return -EBUSY;

    hrtimer_init(&hemac->timerTxWdt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    hemac->timerTxWdt.function = _MDev_EMAC_TxWdt_CB;

    // 在 /sys/class/net 下创建网络设备系统调用文件
    alloc_chrdev_region(&gEthDev, 0, MINOR_EMAC_NUM, "ETH");
    hemac->mstar_class_emac_device = device_create(msys_get_sysfs_class(), NULL, MKDEV(MAJOR(gEthDev), hemac->u8Minor), NULL, hemac->name);
    dev_set_drvdata(hemac->mstar_class_emac_device, (void*)dev);
    device_create_file(hemac->mstar_class_emac_device, &dev_attr_tx_sw_queue_info);
    device_create_file(hemac->mstar_class_emac_device, &dev_attr_dlist_info);
    device_create_file(hemac->mstar_class_emac_device, &dev_attr_reverse_led);
    device_create_file(hemac->mstar_class_emac_device, &dev_attr_check_link_time);
    device_create_file(hemac->mstar_class_emac_device, &dev_attr_check_link_timedis);
    device_create_file(hemac->mstar_class_emac_device, &dev_attr_info);
    device_create_file(hemac->mstar_class_emac_device, &dev_attr_sw_led_flick_speed);
    device_create_file(hemac->mstar_class_emac_device, &dev_attr_turndrv);

    return 0;
}

static int MDev_EMAC_probe (struct net_device *dev)
{
    int detected;
    /* Read the PHY ID registers - try all addresses */
    detected = MDev_EMAC_setup(dev);
    return detected;
}

static int MDev_EMAC_init(struct platform_device *pdev)
{
    struct emac_handle *hemac;
    int ret;
    struct net_device* emac_dev = NULL;

    // 申请一个struct net_device,参数是驱动私有参数的大小,
    // net_device里面包含一个私有数据结构的指针,会一并申请空间
    emac_dev = alloc_etherdev(sizeof(*hemac)); 
    hemac = netdev_priv(emac_dev); // 获取 net_device 里面私有数据的指针

    // 初始化 hemac ... 

    netdev_set_default_ethtool_ops(emac_dev, &sstar_emac_ethtool_ops); // 初始化 ethtool 工具回调函数

    MDev_EMAC_dts(emac_dev); // 读设备树

    // 根据设备树信息申请资源,初始化硬件 。。。

    // NAPI (New API) 在收到硬件中断后,通过poll方式将传输过来的数据包统一处理
    netif_napi_add(emac_dev, &hemac->napi, MDev_EMAC_napi_poll, EMAC_NAPI_WEIGHT);

    emac_dev->netdev_ops = &mstar_lan_netdev_ops; // 指定net_device 的回调函数

    if (MDev_EMAC_probe(emac_dev)) // 探测和初始化 net_device
        goto end;

    // 初始化PHY 。。。

    if ((ret = register_netdev(emac_dev))) // 注册struct net_device
        goto end;

    platform_set_drvdata(pdev, (void*)emac_dev); // struct net_device 作为平台设备的一个私有数据

    return 0;

end:
    skb_queue_destroy(&(hemac->skb_queue_tx));
    free_netdev(emac_dev);
    emac_dev = NULL;
    hemac->initstate = ETHERNET_TEST_INIT_FAIL;
    return -1;
}

static int mstar_emac_drv_probe(struct platform_device *pdev)
{
    int retval=0;

    if (retval = MDev_EMAC_init(pdev)) // 网络设备初始化
        return retval;

    return retval;
}

static struct platform_driver Mstar_emac_driver = {
    .probe      = mstar_emac_drv_probe,
    .remove     = mstar_emac_drv_remove,
    .suspend    = mstar_emac_drv_suspend,
    .resume     = mstar_emac_drv_resume,

    .driver = {
        .name    = "Sstar-emac",
        .of_match_table = mstaremac_of_device_ids,
        .owner  = THIS_MODULE,
    }
};

static int __init mstar_emac_drv_init_module(void)
{
    int retval=0;
    retval = platform_driver_register(&Mstar_emac_driver);
    return retval;
}

static void __exit mstar_emac_drv_exit_module(void)
{
    platform_driver_unregister(&Mstar_emac_driver);
}

module_init(mstar_emac_drv_init_module);
module_exit(mstar_emac_drv_exit_module);

end

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: SSD202D是一款专业的烟雾传感器,可用于家庭、商业、工业等领域的火灾探测和烟雾报警。其PDF文件包含着详细的产品规格、特性及应用场景。在产品规格方面,SSD202D采用悬浮式设计,可检测出更小的烟雾颗粒;它还支持多电压输入,适配不同电源接口;蜂鸣器输出声音强度高达85分贝,能在危险情况下及时报警。在特性方面,SSD202D通过了UL认证和EN54-7标准,证明了其质量和可靠性达到了国际标准。它还具有高度灵敏度、快速响应和防误报等特性,在火灾发生时能够实现准确的探测和报警。在应用场景方面,SSD202D可广泛应用于室内烟雾检测和报警,如住宅、酒店、学校、办公场所、医院等场所。总之,SSD202D是一款高品质、高性能、高可靠性的烟雾传感器,能有效预防火灾事故的发生。 ### 回答2: SSD202D是一款广泛用于汽车电子系统的平行输入输出(PIO)芯片。该芯片具有16位输入和输出端口,可兼容多种不互斥的接口,比如8051、AVR、PIC、ARM等微控制器。SSD202D提供了高性能的时序,能够通过按键输入或输出多种信号并保持数据的一致性。同时,该芯片集成了上拉电阻和输出电流驱动器,节约了来自周围电路的成本和空间。SSD202D的工作电压为2.7V至5.5V,工作温度为-40℃到+125℃,非常适合在恶劣环境下使用。SSD202D的PDF文档提供了完整的器件特性和应用说明,可以帮助技术人员更好地了解和使用该芯片。作为一款经典的PIO芯片,SSD202D在汽车和其他较低复杂度的应用领域中表现优异。 ### 回答3: SSD202D是一款数字式细分步进电机驱动器,具有高性能和稳定性。它可以接受多种输入信号,如脉冲、方向、电平和串行通信等。该驱动器采用先进的DSP控制技术,具有卓越的低速运行性能、高精度定位和静音运行等特点。它可以广泛应用于印刷、电子、纺织、医疗器械和自动化设备等行业。 SSD202D的技术规格包括: 1. 电源电压:24-80伏特DC 2. 峰值电流:2-7安培 3. 工作温度:-10℃到50℃ 4. 最大速度:2000rpm 该产品的PDF文档提供了详细的技术参数和接线图,便于用户进行有效的应用。SSD202D可通过控制器、电位器、PLC或计算机进行控制。用户可以通过按规定的步骤进行简单的设置,以实现各种不同的应用需求。总之,SSD202D是一款优秀的数字式细分步进电机驱动器,具有可靠性、高效性和易于控制等特点,为用户提供了稳定、高精度的电机驱动解决方案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值