为了便于分析,贴出对应的方法
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结构中的成员。问题多多,以后再说