linux网络设备应用与驱动编程学习4——模板与实例(C)——其它方法

 

为了便于分析,贴出对应的方法

ndev->open = lpc32xx_net_open;

       ndev->stop = lpc32xx_net_close;

       ndev->hard_start_xmit = lpc32xx_net_hard_start_xmit;

       ndev->tx_timeout = lpc32xx_net_timeout;

       ndev->watchdog_timeo = msecs_to_jiffies(watchdog);

       ndev->set_multicast_list = lpc32xx_net_set_multicast_list;

       ndev->ethtool_ops = &lpc32xx_net_ethtool_ops;

       ndev->do_ioctl = &lpc32xx_net_ioctl;

#ifdef CONFIG_NET_POLL_CONTROLLER

       ndev->poll_controller = lpc32xx_net_poll_controller;

 

n         hard_start_xmit方法(发送数据时调用)

static int lpc32xx_net_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev)

{

       struct netdata_local *pldat = netdev_priv(ndev);

       unsigned int len, txidx;

       u32 *ptxstat;

       struct txrx_desc_t *ptxrxdesc;

 

       len = skb->len;

//skb的大小

 

       spin_lock_irq(&pldat->lock);

 

       if (pldat->num_used_tx_buffs >= (ENET_TX_DESC - 1))

       {

              /* This function should never be called when there are no

                 buffers, log the error */

              netif_stop_queue(ndev);

              spin_unlock_irq(&pldat->lock);

              dev_err(&pldat->pdev->dev,

                     "BUG! TX request when no free TX buffers!\n");

              return 1;

       }

 

       /* Get the next TX descriptor index */

       txidx = __raw_readl(ENET_TXPRODUCEINDEX(pldat->net_base));

//获得发送描述符的生产者索引

 

       /* Setup control for the transfer */

       ptxstat = (u32 *) pldat->tx_stat_v [txidx];

       *ptxstat = 0;

//将描述符状态写为0  

ptxrxdesc = (struct txrx_desc_t *) pldat->tx_desc_v [txidx];

       ptxrxdesc->control = (len - 1) | 0xC0000000;

//描述符的控制字,写入skb的长度,最后希望发送完成后,产生中断。

 

       /* Copy data to the DMA buffer */

       memcpy((void *) pldat->tx_buff_v [txidx], skb->data, len);

//将skb拷贝到DMA的数据缓冲区中。

       /* Save the buffer and increment the buffer counter */

       pldat->skb[txidx] = skb;

       pldat->num_used_tx_buffs++;

 

       /* Start transmit */

       txidx++;

       if (txidx >= ENET_TX_DESC)

       {

              txidx = 0;

       }

//生产者索引到最大值时应回0

       __raw_writel((u32) txidx, ENET_TXPRODUCEINDEX(pldat->net_base));

//更新发送描述符的生产者索引应由软件完成。

 

       /* Stop queue if no more TX buffers */

       if (pldat->num_used_tx_buffs >= (ENET_TX_DESC - 1))

       {

              netif_stop_queue(ndev);

       }

 

       spin_unlock_irq(&pldat->lock);

       ndev->trans_start = jiffies;

//记录发送的时间戳

 

       return 0;

}

这个函数只发送了一个描述符,然后更新了一次发送生产者索引,一个描述符对应一个skb

n         tx_timeout(发送超时时调用)

static void lpc32xx_net_timeout(struct net_device *ndev)

{

       struct netdata_local *pldat = netdev_priv(ndev);

 

       /* This should never happen and indicates a problem */

       dev_err(&pldat->pdev->dev, "BUG! TX timeout occurred!\n");

}

这个函数从实现的结果看,它只是调试方便才实现的。

n         watchdog_timeo(设置超时时间)

n         set_multicast_list(组播列表改变或设备标志改变时)

static void lpc32xx_net_set_multicast_list(struct net_device *ndev)

{

       struct netdata_local *pldat = netdev_priv(ndev);

       struct dev_mc_list *mcptr = ndev->mc_list;

//组播mac地址

       int i, mc_cnt = ndev->mc_count;

//组播(地址)数

       u32 tmp32, hash_val, hashlo, hashhi;

       unsigned long flags;

 

       spin_lock_irqsave(&pldat->lock, flags);

 

       /* Set station address */

       __lpc32xx_set_mac(pldat, ndev->dev_addr);

//将dev_addr中的MAC地址存入站寄存器

 

       tmp32 =  RXFLTRW_ACCEPTUBROADCAST | RXFLTRW_ACCEPTPERFECT;

 

       if (ndev->flags & IFF_PROMISC)

       {

              tmp32 |= RXFLTRW_ACCEPTUNICAST | RXFLTRW_ACCEPTUNICASTHASH |

                     RXFLTRW_ACCEPTUMULTICASTHASH;

       }

       if (ndev->flags & IFF_ALLMULTI)

       {

              tmp32 |= RXFLTRW_ACCEPTUMULTICAST;

       }

       __raw_writel(tmp32, ENET_RXFILTER_CTRL(pldat->net_base));

//设置滤波存器

 

 

       /* Set initial hash table */

       hashlo = 0x0;

       hashhi = 0x80000000;

 

       /* 64 bits : multicast address in hash table */

       for (i = 0; i < mc_cnt; i++, mcptr = mcptr->next)

       {

              hash_val = ether_crc_le(6, mcptr->dmi_addr) & 0x3f;

 

              if (hash_val >= 32)

              {

                     hashhi |= 1 << (32 - hash_val);

              }

              else

              {

                     hashlo |= 1 << hash_val;

              }

       }

 

       __raw_writel(hashlo, ENET_HASHFILTERL(pldat->net_base));

       __raw_writel(hashhi, ENET_HASHFILTERH(pldat->net_base));

//填充Hash滤波器表

 

       spin_unlock_irqrestore(&pldat->lock, flags);

}

总结:这个函数重填了MAC地址,根据标志重设了滤波寄存器,重填了 hash滤波表。

n         ethtool_ops(更改或报告网络设备的设置)

static const struct ethtool_ops lpc32xx_net_ethtool_ops = {

       .get_drvinfo    = lpc32xx_net_ethtool_getdrvinfo,

       .get_settings   = lpc32xx_net_ethtool_getsettings,

       .set_settings    = lpc32xx_net_ethtool_setsettings,

       .get_msglevel  = lpc32xx_net_ethtool_getmsglevel,

       .set_msglevel  = lpc32xx_net_ethtool_setmsglevel,

       .get_link  = ethtool_op_get_link,

};

lpc32xx_net_ethtool_getdrvinfo,用于获得驱动的信息,如驱动版本,总线等

lpc32xx_net_ethtool_getsettings,lpc32xx_net_ethtool_setsettings分别用于获得和设置一些信息。视命令而定

ethtool_op_get_link,获知网络连接状态的

 

 

除了以上方法,还有中断

static irqreturn_t __lpc32xx_eth_interrupt(int irq, void *dev_id)

{

       struct net_device *ndev = dev_id;

       struct netdata_local *pldat = netdev_priv(ndev);

       u32 tmp;

 

       spin_lock(&pldat->lock);

 

       /* Get the interrupt status */

       tmp = __raw_readl(ENET_INTSTATUS(pldat->net_base));

/*每当发生中断时,执行此函数,此函数先查询中断状态寄存器*/

       while (tmp)

       {

              /* Clear interrupts */

              __raw_writel(tmp, ENET_INTCLEAR(pldat->net_base));

 

              /* Transmit complete? */

              if (tmp & (MACINT_TXUNDERRUNINTEN | MACINT_TXERRORINTEN |

                     MACINT_TXFINISHEDINTEN | MACINT_TXDONEINTEN))

/*如果发生了重大的溢出错误,发送出现错误,单个或所有描述符发送完成都会调用以下函数*/

              {

                     __lpc32xx_handle_xmit(ndev);

              }

 

              /* Receive buffer available */

              if (tmp & (MACINT_RXOVERRUNINTEN | MACINT_RXERRORONINT |

                     MACINT_RXFINISHEDINTEN | MACINT_RXDONEINTEN))

              {

                     __lpc32xx_handle_recv(ndev);

              }

 

              /* Recheck the interrupt status */

              tmp = __raw_readl(ENET_INTSTATUS(pldat->net_base));

       }

 

       spin_unlock(&pldat->lock);

 

       return IRQ_HANDLED;

}

函数处理了发送与接收中断。

 

 

追踪__lpc32xx_handle_xmit

static void __lpc32xx_handle_xmit(struct net_device *ndev)

{

       struct netdata_local *pldat = netdev_priv(ndev);

       struct sk_buff *skb;

       unsigned int txcidx, *ptxstat, txstat;

 

       txcidx = __raw_readl(ENET_TXCONSUMEINDEX(pldat->net_base));

//获得发送消费者索引(要发送的描述符)

       while (pldat->last_tx_idx != txcidx)

       {

              skb = (struct sk_buff *) pldat->skb[pldat->last_tx_idx];

//获得最近已发送描述符对应的skb

              /* A buffer is available, get buffer status */

              ptxstat = (unsigned int *) pldat->tx_stat_v[pldat->last_tx_idx];

              txstat = *ptxstat;

//将上一次发送的描述符状态赋给txstat.

              /* Next buffer and decrement used buffer counter */

              pldat->num_used_tx_buffs--;

              pldat->last_tx_idx++;

              if (pldat->last_tx_idx >= ENET_TX_DESC)

              {

                     pldat->last_tx_idx = 0;

              }

 

              /* Update collision counter */

              ndev->stats.collisions += ((txstat >> 21) & 0xF);

//更新总的冲突次数

 

              /* Any errors occurred? */

              if (txstat & 0x80000000)

              {

                     if (txstat & 0x20000000)

                     {

                            /* FIFO underrun */

                            ndev->stats.tx_fifo_errors++;

                            ndev->stats.tx_errors++;

                     }

                     if (txstat & 0x10000000)

                     {

                            /* Late collision */

                            ndev->stats.tx_aborted_errors++;

                            ndev->stats.tx_errors++;

                     }

                     if (txstat & 0x08000000)

                     {

                            /* Excessive collision */

                            ndev->stats.tx_aborted_errors++;

                            ndev->stats.tx_errors++;

                     }

                     if (txstat & 0x04000000)

                     {

                            /* Defer limit */

                            ndev->stats.tx_aborted_errors++;

                            ndev->stats.tx_errors++;

                     }

/*更新发送描述符过程中的各种错误统计*/

 

                     /* Buffer transmit failed, requeue it */

                     lpc32xx_net_hard_start_xmit(skb, ndev);

//统计完这些信息后,重发。

              }

              else

              {

                     /* Update stats */

                     ndev->stats.tx_packets++;

                     ndev->stats.tx_bytes += skb->len;

 

                     /* Free buffer */

                     dev_kfree_skb_irq(skb);

//如果没有错误就更新数据缓冲区指针,释放刚使用的skb构。

              }

             

              txcidx = __raw_readl(ENET_TXCONSUMEINDEX(pldat->net_base));

       }

 

       if (netif_queue_stopped(ndev))

       {

              netif_wake_queue(ndev);

//重新启动发送队列

       }

}

总结:__lpc32xx_handle_xmit,出现underrun,latecollision,excessivecollision,,excessivedefer错误时,统计错误信息,重新发送目前的skb。或是其它的中断如txfinishedint和txdoneint则更新发送状态。(似乎对于NoDescriptor这样的中断是按正常中断处理的)

 

追踪__lpc32xx_handle_recv

static void __lpc32xx_handle_recv(struct net_device *ndev)

{

       struct netdata_local *pldat = netdev_priv(ndev);

       struct sk_buff *skb;

       int rxconsidx, len, ethst;

       struct rx_status_t *prxstat;

       u8 *prdbuf;

 

       /* Get the current RX buffer indexes */

       rxconsidx = (int) __raw_readl(ENET_RXCONSUMEINDEX(pldat->net_base));

//获得接收消费者索引

       while (rxconsidx != (int) __raw_readl(ENET_RXPRODUCEINDEX(pldat->net_base)))

//当描述符数组不为空时

       {

              /* Get pointer to receive status */

              prxstat = (struct rx_status_t *) pldat->rx_stat_v [rxconsidx];

              len = (prxstat->statusinfo & 0x7FF) + 1;

//获得要处理的描述符的状态和数据缓冲区的字节数

 

              /* Status error? */

              ethst = prxstat->statusinfo;

              if ((ethst & 0xBF800000) == 0x84000000)

              {

                     /* Range error, can be ignored */

                     ethst &= ~0x80000000;

              }

忽略“接收的包超出包最大限制”的错误。

 

              if (ethst & 0x80000000)

              {

                     /* Check statuses */

                     if (prxstat->statusinfo & (1 << 28))

                     {

                            /* Overrun error */

                            ndev->stats.rx_fifo_errors++;

                     }

                     else if (prxstat->statusinfo & (1 << 23))

                     {

                            /* CRC error */

                            ndev->stats.rx_crc_errors++;

                     }

                     else if (prxstat->statusinfo & (1 << 25))

                     {

                            /* Length error */

                            ndev->stats.rx_length_errors++;

                     }

                     else if (prxstat->statusinfo & 0x80000000)

                     {

                            /* Other error */

                            ndev->stats.rx_length_errors++;

                     }

                     ndev->stats.rx_errors++;

              }

//如果状态字出现错误,则统计错误信息。

              else

              {

                     /* Packet is good */

                     skb = dev_alloc_skb(len + 8);

//申请一个skb结构体,大小为接收的skb的大小再加8

                     if (!skb)

                     {

                            ndev->stats.rx_dropped++;

                     }

                     else

                     {

                            skb_reserve(skb, 8);

//将skb后移8个字节。

                            prdbuf = skb_put(skb, (len - 0));

//skb尾部增加len大小。

                            /* Copy packer from buffer */

                            memcpy(prdbuf, (void *) pldat->rx_buff_v [rxconsidx], len);

//将接收描述符拷贝入skb结构中。

                            /* Pass to upper layer */

                            skb->protocol = eth_type_trans(skb, ndev);

                            netif_rx(skb);

                            ndev->last_rx = jiffies;

                            ndev->stats.rx_packets++;

                            ndev->stats.rx_bytes += len;

                     }

              }

 

              /* Increment consume index */

              rxconsidx = rxconsidx + 1;

              if (rxconsidx >= ENET_RX_DESC)

              {

                     rxconsidx = 0;

              }

              __raw_writel((u32) rxconsidx, ENET_RXCONSUMEINDEX(pldat->net_base));

       }

}

//最后更新接收描述符的消费索引

总结:依照对__lpc32xx_handle_recv的分析,它只能对错误信息进行统计,然后会继续接收新的描述符。所谓接收处理即将接收描述符的数据指针指向的数据定入申请的skb,然后发送给上一层协议。

 

为了分析的完整性,还有平台的resume,remove,suspend方法。

static struct platform_driver lpc32xx_net_driver = {

       .probe            = lpc32xx_net_drv_probe,

       .remove          = __devexit_p(lpc32xx_net_drv_remove),

       .suspend  = lpc32xx_net_drv_suspend,

       .resume          = lpc32xx_net_drv_resume,

       .driver            = {

              .name      = MODNAME,

       },

};

 

static int lpc32xx_net_drv_remove(struct platform_device *pdev)

{

       struct net_device *ndev = platform_get_drvdata(pdev);

       struct netdata_local *pldat = netdev_priv(ndev);

 

       unregister_netdev(ndev);//注销ndev

       platform_set_drvdata(pdev, NULL);//释放pdev的drvdata

       dma_free_coherent(&pldat->pdev->dev, pldat->dma_buff_size,

              (void *) pldat->dma_buff_base_v, (dma_addr_t) pldat->dma_buff_base_p);

//释放dma一致性缓冲区。

       free_irq(ndev->irq, ndev);//释放中断

       iounmap(pldat->net_base);//去除寄存器映射

       clk_disable(pldat->clk);

       clk_put(pldat->clk);//释放时钟

       free_netdev(ndev);//释放ndev

 

       return 0;

}

 

static int lpc32xx_net_drv_suspend(struct platform_device *pdev, pm_message_t state)

{

       struct net_device *ndev = platform_get_drvdata(pdev);

       struct netdata_local *pldat = netdev_priv(ndev);

 

       if (ndev)

       {

              if (netif_running(ndev))

//推测设备是否停下来。

              {

                     netif_device_detach(ndev);

//断开网络设备

                     __lpc32xx_net_shutdown(pldat);

//重启以太网块,复位MAC配置寄存器

                     clk_disable(pldat->clk);

              }

       }

 

       return 0;

}

 

static int lpc32xx_net_drv_resume(struct platform_device *pdev)

{

       struct net_device *ndev = platform_get_drvdata(pdev);

       struct netdata_local *pldat;

 

       if (ndev)

       {

              if (netif_running(ndev))

              {

                     pldat = netdev_priv(ndev);

 

                     /* Enable interface clock */

                     clk_enable(pldat->clk);

//获得时钟

 

                     /* Reset and initialize */

                     __lpc32xx_eth_reset(pldat);

                     __lpc32xx_eth_init(pldat);

 

                     netif_device_attach(ndev);

//连接网络设备

              }

       }

 

       return 0;

}

 

总结:网络设备驱动也是有自己的框架,要做的就是填充ndev的成员和操作函数,然后处理好中断接收。还有一个工作就是错误处理和信息统计。

 

在分析lpc32xx_mii.c时,也遇到一些问题:比如mdiobus_register()看似是注册总线,实际上注册的是设备。而且注册的设备,像字符设备一样,拥有自己一系列的操作方法。内核中,给出了一种名为mdio_bus的总线,通过mdiobus_register注册的设备在设备模型上隶属于mdio_bus,但是实际却并没有相应的驱动。这样的设备本身拥有mii_bus总线,问题是这个总线却没有向内核注册。该设备上的方法就是mii_bus结构中的成员。问题多多,以后再说

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值