1、上一章节我们已经分析到中断处理函数会调度一个工作队列,将更多的处理交给了一个work去处理。接下来我们进一步分析中断下半部都做了些什么处理。
static void enc28j60_irq_work_handler(struct work_struct *work)
{
struct enc28j60_net *priv =
container_of(work, struct enc28j60_net, irq_work);
struct net_device *ndev = priv->netdev;
int intflags, loop;
if (netif_msg_intr(priv))
printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);
/* disable further interrupts */
locked_reg_bfclr(priv, EIE, EIE_INTIE);
do {
loop = 0;
intflags = locked_regb_read(priv, EIR);
/* DMA interrupt handler (not currently used) */
if ((intflags & EIR_DMAIF) != 0) {
loop++;
if (netif_msg_intr(priv))
printk(KERN_DEBUG DRV_NAME
": intDMA(%d)\n", loop);
locked_reg_bfclr(priv, EIR, EIR_DMAIF);
}
/* LINK changed handler */
if ((intflags & EIR_LINKIF) != 0) {
loop++;
if (netif_msg_intr(priv))
printk(KERN_DEBUG DRV_NAME
": intLINK(%d)\n", loop);
enc28j60_check_link_status(ndev);
/* read PHIR to clear the flag */
enc28j60_phy_read(priv, PHIR);
}
/* TX complete handler */
if (((intflags & EIR_TXIF) != 0) &&
((intflags & EIR_TXERIF) == 0)) {
bool err = false;
loop++;
if (netif_msg_intr(priv))
printk(KERN_DEBUG DRV_NAME
": intTX(%d)\n", loop);
priv->tx_retry_count = 0;
if (locked_regb_read(priv, ESTAT) & ESTAT_TXABRT) {
if (netif_msg_tx_err(priv))
dev_err(&ndev->dev,
"Tx Error (aborted)\n");
err = true;
}
if (netif_msg_tx_done(priv)) {
u8 tsv[TSV_SIZE];
enc28j60_read_tsv(priv, tsv);
enc28j60_dump_tsv(priv, "Tx Done", tsv);
}
enc28j60_tx_clear(ndev, err);
locked_reg_bfclr(priv, EIR, EIR_TXIF);
}
/* TX Error handler */
if ((intflags & EIR_TXERIF) != 0) {
u8 tsv[TSV_SIZE];
loop++;
if (netif_msg_intr(priv))
printk(KERN_DEBUG DRV_NAME
": intTXErr(%d)\n", loop);
locked_reg_bfclr(priv, ECON1, ECON1_TXRTS);
enc28j60_read_tsv(priv, tsv);
if (netif_msg_tx_err(priv))
enc28j60_dump_tsv(priv, "Tx Error", tsv);
/* Reset TX logic */
mutex_lock(&priv->lock);
nolock_reg_bfset(priv, ECON1, ECON1_TXRST);
nolock_reg_bfclr(priv, ECON1, ECON1_TXRST);
nolock_txfifo_init(priv, TXSTART_INIT, TXEND_INIT);
mutex_unlock(&priv->lock);
/* Transmit Late collision check for retransmit */
if (TSV_GETBIT(tsv, TSV_TXLATECOLLISION)) {
if (netif_msg_tx_err(priv))
printk(KERN_DEBUG DRV_NAME
": LateCollision TXErr (%d)\n",
priv->tx_retry_count);
if (priv->tx_retry_count++ < MAX_TX_RETRYCOUNT)
locked_reg_bfset(priv, ECON1,
ECON1_TXRTS);
else
enc28j60_tx_clear(ndev, true);
} else
enc28j60_tx_clear(ndev, true);
locked_reg_bfclr(priv, EIR, EIR_TXERIF | EIR_TXIF);
}
/* RX Error handler */
if ((intflags & EIR_RXERIF) != 0) {
loop++;
if (netif_msg_intr(priv))
printk(KERN_DEBUG DRV_NAME
": intRXErr(%d)\n", loop);
/* Check free FIFO space to flag RX overrun */
if (enc28j60_get_free_rxfifo(priv) <= 0) {
if (netif_msg_rx_err(priv))
printk(KERN_DEBUG DRV_NAME
": RX Overrun\n");
ndev->stats.rx_dropped++;
}
locked_reg_bfclr(priv, EIR, EIR_RXERIF);
}
/* RX handler */
if (enc28j60_rx_interrupt(ndev))
loop++;
} while (loop);
/* re-enable interrupts */
locked_reg_bfset(priv, EIE, EIE_INTIE);
if (netif_msg_intr(priv))
printk(KERN_DEBUG DRV_NAME ": %s() exit\n", __func__);
}
在中断下半部处理函数中,会读取读取中断寄存器EIR来判断是哪一类中断,总共有这么几类中断:
EIR_DMAIF:DMA复制或校验和计算已完成
EIR_LINKIF:连接状态发生改变产生中断
EIR_TXIF: 发送完成中断
EIR_TXERIF:发送错误中断
EIR_RXERIF:接收错误中断
上述这些中断都是一些状态以及异常情况的中断,还有一个最重要的中断就是接收的数据包后产生中断。
static int enc28j60_rx_interrupt(struct net_device *ndev)
{
struct enc28j60_net *priv = netdev_priv(ndev);
int pk_counter, ret;
pk_counter = locked_regb_read(priv, EPKTCNT);
if (pk_counter && netif_msg_intr(priv))
printk(KERN_DEBUG DRV_NAME ": intRX, pk_cnt: %d\n", pk_counter);
if (pk_counter > priv->max_pk_counter) {
/* update statistics */
priv->max_pk_counter = pk_counter;
if (netif_msg_rx_status(priv) && priv->max_pk_counter > 1)
printk(KERN_DEBUG DRV_NAME ": RX max_pk_cnt: %d\n",
priv->max_pk_counter);
}
ret = pk_counter;
while (pk_counter-- > 0)
enc28j60_hw_rx(ndev);
return ret;
}
代码中通过读取寄存器EPKTCNT
获取有多少数据包待处理,然后通过调用enc28j60_hw_rx
处理接收到的数据包。
2、报文接收处理
/*
* Hardware receive function.
* Read the buffer memory, update the FIFO pointer to free the buffer,
* check the status vector and decrement the packet counter.
*/
static void enc28j60_hw_rx(struct net_device *ndev)
{
struct enc28j60_net *priv = netdev_priv(ndev);
struct sk_buff *skb = NULL;
u16 erxrdpt, next_packet, rxstat;
u8 rsv[RSV_SIZE];
int len;
if (netif_msg_rx_status(priv))
printk(KERN_DEBUG DRV_NAME ": RX pk_addr:0x%04x\n",
priv->next_pk_ptr);
if (unlikely(priv->next_pk_ptr > RXEND_INIT)) {
if (netif_msg_rx_err(priv))
dev_err(&ndev->dev,
"%s() Invalid packet address!! 0x%04x\n",
__func__, priv->next_pk_ptr);
/* packet address corrupted: reset RX logic */
mutex_lock(&priv->lock);
nolock_reg_bfclr(priv, ECON1, ECON1_RXEN);
nolock_reg_bfset(priv, ECON1, ECON1_RXRST);
nolock_reg_bfclr(priv, ECON1, ECON1_RXRST);
nolock_rxfifo_init(priv, RXSTART_INIT, RXEND_INIT);
nolock_reg_bfclr(priv, EIR, EIR_RXERIF);
nolock_reg_bfset(priv, ECON1, ECON1_RXEN);
mutex_unlock(&priv->lock);
ndev->stats.rx_errors++;
return;
}
/* Read next packet pointer and rx status vector */
enc28j60_mem_read(priv, priv->next_pk_ptr, sizeof(rsv), rsv);
next_packet = rsv[1];
next_packet <<= 8;
next_packet |= rsv[0];
len = rsv[3];
len <<= 8;
len |= rsv[2];
rxstat = rsv[5];
rxstat <<= 8;
rxstat |= rsv[4];
if (netif_msg_rx_status(priv))
enc28j60_dump_rsv(priv, __func__, next_packet, len, rxstat);
if (!RSV_GETBIT(rxstat, RSV_RXOK) || len > MAX_FRAMELEN) {
if (netif_msg_rx_err(priv))
dev_err(&ndev->dev, "Rx Error (%04x)\n", rxstat);
ndev->stats.rx_errors++;
if (RSV_GETBIT(rxstat, RSV_CRCERROR))
ndev->stats.rx_crc_errors++;
if (RSV_GETBIT(rxstat, RSV_LENCHECKERR))
ndev->stats.rx_frame_errors++;
if (len > MAX_FRAMELEN)
ndev->stats.rx_over_errors++;
} else {
skb = netdev_alloc_skb(ndev, len + NET_IP_ALIGN);
if (!skb) {
if (netif_msg_rx_err(priv))
dev_err(&ndev->dev,
"out of memory for Rx'd frame\n");
ndev->stats.rx_dropped++;
} else {
skb_reserve(skb, NET_IP_ALIGN);
/* copy the packet from the receive buffer */
enc28j60_mem_read(priv,
rx_packet_start(priv->next_pk_ptr),
len, skb_put(skb, len));
if (netif_msg_pktdata(priv))
dump_packet(__func__, skb->len, skb->data);
skb->protocol = eth_type_trans(skb, ndev);
/* update statistics */
ndev->stats.rx_packets++;
ndev->stats.rx_bytes += len;
netif_rx_ni(skb);
}
}
/*
* Move the RX read pointer to the start of the next
* received packet.
* This frees the memory we just read out
*/
erxrdpt = erxrdpt_workaround(next_packet, RXSTART_INIT, RXEND_INIT);
if (netif_msg_hw(priv))
printk(KERN_DEBUG DRV_NAME ": %s() ERXRDPT:0x%04x\n",
__func__, erxrdpt);
mutex_lock(&priv->lock);
nolock_regw_write(priv, ERXRDPTL, erxrdpt);
#ifdef CONFIG_ENC28J60_WRITEVERIFY
if (netif_msg_drv(priv)) {
u16 reg;
reg = nolock_regw_read(priv, ERXRDPTL);
if (reg != erxrdpt)
printk(KERN_DEBUG DRV_NAME ": %s() ERXRDPT verify "
"error (0x%04x - 0x%04x)\n", __func__,
reg, erxrdpt);
}
#endif
priv->next_pk_ptr = next_packet;
/* we are done with this packet, decrement the packet counter */
nolock_reg_bfset(priv, ECON2, ECON2_PKTDEC);
mutex_unlock(&priv->lock);
}
上述代码中最重要的是下面这一段
{
skb = netdev_alloc_skb(ndev, len + NET_IP_ALIGN);
if (!skb) {
if (netif_msg_rx_err(priv))
dev_err(&ndev->dev,
"out of memory for Rx'd frame\n");
ndev->stats.rx_dropped++;
} else {
skb_reserve(skb, NET_IP_ALIGN);
/* copy the packet from the receive buffer */
enc28j60_mem_read(priv,
rx_packet_start(priv->next_pk_ptr),
len, skb_put(skb, len));
if (netif_msg_pktdata(priv))
dump_packet(__func__, skb->len, skb->data);
skb->protocol = eth_type_trans(skb, ndev);
/* update statistics */
ndev->stats.rx_packets++;
ndev->stats.rx_bytes += len;
netif_rx_ni(skb);
}
}
上面这段代码中通过skb = netdev_alloc_skb(ndev, len + NET_IP_ALIGN);
申请了一个sk_buffer,这是一个非常重要的结构体。与内核协议栈通信就需要构造这么一个结构体上传。
enc28j60_mem_read(priv, rx_packet_start(priv->next_pk_ptr), len, skb_put(skb, len));
这是将数据从网卡的memory里读出来,保存到sk_buffer里面的内存空间里。netif_rx_ni(skb);
调用内核的接口,将构造好的sk_buffer
发送给内核协议栈,至此整个接收报文流程完毕。
3、报文发送
报文发送是由上层协议栈调用我们驱动注册的函数接口来发包的,不知道大家是否还记得我们之前在probe过程的时候,动态申请了net_device
结构体,并对其填充了netdev_ops
,填充的内容如下:
static const struct net_device_ops enc28j60_netdev_ops = {
.ndo_open = enc28j60_net_open,
.ndo_stop = enc28j60_net_close,
.ndo_start_xmit = enc28j60_send_packet,
.ndo_set_rx_mode = enc28j60_set_multicast_list,
.ndo_set_mac_address = enc28j60_set_mac_address,
.ndo_tx_timeout = enc28j60_tx_timeout,
.ndo_validate_addr = eth_validate_addr,
};
ndo_start_xmit
这个函数指针是用来发送报文用的。发送函数如下:
static netdev_tx_t enc28j60_send_packet(struct sk_buff *skb,
struct net_device *dev)
{
struct enc28j60_net *priv = netdev_priv(dev);
if (netif_msg_tx_queued(priv))
printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);
/* If some error occurs while trying to transmit this
* packet, you should return '1' from this function.
* In such a case you _may not_ do anything to the
* SKB, it is still owned by the network queueing
* layer when an error is returned. This means you
* may not modify any SKB fields, you may not free
* the SKB, etc.
*/
netif_stop_queue(dev);
/* Remember the skb for deferred processing */
priv->tx_skb = skb;
schedule_work(&priv->tx_work);
return NETDEV_TX_OK;
}
该函数中,一进来就使用netif_stop_queue来禁止上层应用再对该网络设备发包。然后又调度了一个工作队列来进一步处理。
之前probe过程时将tx_work
与enc28j60_tx_work_handler
绑定;
INIT_WORK(&priv->tx_work, enc28j60_tx_work_handler);
static void enc28j60_tx_work_handler(struct work_struct *work)
{
struct enc28j60_net *priv =
container_of(work, struct enc28j60_net, tx_work);
/* actual delivery of data */
enc28j60_hw_tx(priv);
}
需要继续看enc28j60_hw_tx
函数。
/*
* Hardware transmit function.
* Fill the buffer memory and send the contents of the transmit buffer
* onto the network
*/
static void enc28j60_hw_tx(struct enc28j60_net *priv)
{
BUG_ON(!priv->tx_skb);
if (netif_msg_tx_queued(priv))
printk(KERN_DEBUG DRV_NAME
": Tx Packet Len:%d\n", priv->tx_skb->len);
if (netif_msg_pktdata(priv))
dump_packet(__func__,
priv->tx_skb->len, priv->tx_skb->data);
enc28j60_packet_write(priv, priv->tx_skb->len, priv->tx_skb->data);
#ifdef CONFIG_ENC28J60_WRITEVERIFY
/* readback and verify written data */
if (netif_msg_drv(priv)) {
int test_len, k;
u8 test_buf[64]; /* limit the test to the first 64 bytes */
int okflag;
test_len = priv->tx_skb->len;
if (test_len > sizeof(test_buf))
test_len = sizeof(test_buf);
/* + 1 to skip control byte */
enc28j60_mem_read(priv, TXSTART_INIT + 1, test_len, test_buf);
okflag = 1;
for (k = 0; k < test_len; k++) {
if (priv->tx_skb->data[k] != test_buf[k]) {
printk(KERN_DEBUG DRV_NAME
": Error, %d location differ: "
"0x%02x-0x%02x\n", k,
priv->tx_skb->data[k], test_buf[k]);
okflag = 0;
}
}
if (!okflag)
printk(KERN_DEBUG DRV_NAME ": Tx write buffer, "
"verify ERROR!\n");
}
#endif
/* set TX request flag */
locked_reg_bfset(priv, ECON1, ECON1_TXRTS);
}
在这个函数中与linux网络架构相关的就是
enc28j60_packet_write(priv, priv->tx_skb->len, priv->tx_skb->data);
从该句话中可以获知,sk_buffer
中的len保存发送长度,data保存数据的起始位置。该函数中其他代码是跟该网卡驱动相关的硬件操作,不做太详细的分析。
到这里网卡已经可以将该数据包发出去,但是有一点需要考虑的就是,协议栈发给网卡驱动的sk_buffer
应该由谁来释放?不释放就会造成内存泄漏。如果由协议栈释放,那么可能会出现驱动正在用sk_buffer里面的数据,结果协议栈就给释放了,要挨打的。那么只能由驱动来释放了。一般网卡都会有一个数据发送完毕的事件。在此时可以对sk_buffer
来释放。在enc28j60中存在一个发送完成的中断,可以使用此中断进行sk_buffer
的回收操作。
还有就是之前调用了netif_stop_queue(dev);
来禁止上层再发包,此时也应该使能发包。
/* TX complete handler */
if (((intflags & EIR_TXIF) != 0) &&
((intflags & EIR_TXERIF) == 0)) {
bool err = false;
loop++;
if (netif_msg_intr(priv))
printk(KERN_DEBUG DRV_NAME
": intTX(%d)\n", loop);
priv->tx_retry_count = 0;
if (locked_regb_read(priv, ESTAT) & ESTAT_TXABRT) {
if (netif_msg_tx_err(priv))
dev_err(&ndev->dev,
"Tx Error (aborted)\n");
err = true;
}
if (netif_msg_tx_done(priv)) {
u8 tsv[TSV_SIZE];
enc28j60_read_tsv(priv, tsv);
enc28j60_dump_tsv(priv, "Tx Done", tsv);
}
enc28j60_tx_clear(ndev, err);
locked_reg_bfclr(priv, EIR, EIR_TXIF);
上面这段代码为中断处理下半部的一部分代码,专门处理发送完成中断。这里最重要的是enc28j60_tx_clear
函数。
static void enc28j60_tx_clear(struct net_device *ndev, bool err)
{
struct enc28j60_net *priv = netdev_priv(ndev);
if (err)
ndev->stats.tx_errors++;
else
ndev->stats.tx_packets++;
if (priv->tx_skb) {
if (!err)
ndev->stats.tx_bytes += priv->tx_skb->len;
dev_kfree_skb(priv->tx_skb);
priv->tx_skb = NULL;
}
locked_reg_bfclr(priv, ECON1, ECON1_TXRTS);
netif_wake_queue(ndev);
}
在enc28j60_tx_clear
函数中,对发送成功和发送失败进行了统计。调用dev_kfree_skb
释放之前的sk_buffer
内存空间。
调用netif_wake_queue
来使能上层发包。
至此,发包流程结束。