DM9000网卡驱动详细分析(3)

 

struct net_device_ops dm9000_netdev_ops结构中包含网卡的打开,关闭,发送数据,改变MTU参数等重要功能:

 

static const struct net_device_ops dm9000_netdev_ops = {

.ndo_open = dm9000_open,

.ndo_stop = dm9000_stop,

.ndo_start_xmit = dm9000_start_xmit,

.ndo_tx_timeout = dm9000_timeout,

.ndo_set_multicast_list = dm9000_hash_table, //设置DM9000网卡的多播地址

.ndo_do_ioctl = dm9000_ioctl,

.ndo_change_mtu = eth_change_mtu,

.ndo_validate_addr = eth_validate_addr,

.ndo_set_mac_address = eth_mac_addr,

#ifdef CONFIG_NET_POLL_CONTROLLER

.ndo_poll_controller = dm9000_poll_controller,

#endif

};

 

/* try reading the node address from the attached EEPROM */

for (i = 0; i < 6; i += 2)

dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);

 

if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {

mac_src = "platform data";

memcpy(ndev->dev_addr, pdata->dev_addr, 6);

}

 

if (!is_valid_ether_addr(ndev->dev_addr)) {

/* try reading from mac */

 

mac_src = "chip";

for (i = 0; i < 6; i++)

ndev->dev_addr[i] = ior(db, i+DM9000_PAR);

}

 

if (!is_valid_ether_addr(ndev->dev_addr))

dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "

"set using ifconfig/n", ndev->name);

 

以上将MAC地址从EEPROM中读取出来并赋值给net_device中的dev_addr成员.

 

platform_set_drvdata(pdev, ndev); // 具体为:&(p->dev)->p->driver_data = ndev, 建立数据结构间的关系

 

ret = register_netdev(ndev); //向内核注册net_device结构

 

后面的代码就是出错处理了.至此dm9000_probe函数分析完.下面着重分析网卡的打开,关闭,数据的发送,接收等功能.

 

//网卡打开:

 

static int dm9000_open(struct net_device *dev) //用ifconfig命令可以打开网卡

{

board_info_t *db = netdev_priv(dev); //根据前面建立的数据关系获取私有结构体指针

unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK;

 

if (netif_msg_ifup(db))

dev_dbg(db->dev, "enabling %s/n", dev->name);

 

/* If there is no IRQ type specified, default to something that

* may work, and tell the user that this is a problem */

 

if (irqflags == IRQF_TRIGGER_NONE)

dev_warn(db->dev, "WARNING: no IRQ resource flags set./n");

 

irqflags |= IRQF_SHARED; //设置中断标志

 

if (request_irq(dev->irq, &dm9000_interrupt, irqflags, dev->name, dev)) //申请中断

return -EAGAIN;

 

/* Initialize DM9000 board */

dm9000_reset(db); //网卡复位

dm9000_init_dm9000(dev); //网卡的初始化

 

/* Init driver variable */

db->dbug_cnt = 0;

 

mii_check_media(&db->mii, netif_msg_link(db), 1);

netif_start_queue(dev); //启动发送队列

 

dm9000_schedule_poll(db);

 

return 0;

}

 

//网卡关闭:

 

static int dm9000_stop(struct net_device *ndev)

{

board_info_t *db = netdev_priv(ndev);

 

if (netif_msg_ifdown(db))

dev_dbg(db->dev, "shutting down %s/n", ndev->name);

 

cancel_delayed_work_sync(&db->phy_poll);

 

netif_stop_queue(ndev); //停止队列

netif_carrier_off(ndev); //停止载波检测

 

/* free interrupt */

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

 

dm9000_shutdown(ndev); //网卡关闭

 

return 0;

}

 

//数据发送:

 

static int dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)

{

unsigned long flags;

board_info_t *db = netdev_priv(dev);

 

dm9000_dbg(db, 3, "%s:/n", __func__);

 

if (db->tx_pkt_cnt > 1) //如果DM9000网卡的TX SRAM中等待发送的数据包个数 >= 2个,则返回,因为不能再往TX SRAM中写入数据了.

return NETDEV_TX_BUSY;

 

spin_lock_irqsave(&db->lock, flags); //禁止中断

 

/* Move data to DM9000 TX RAM */

writeb(DM9000_MWCMD, db->io_addr); //发送写SRAM的命令

 

(db->outblk)(db->io_data, skb->data, skb->len); //将skb->data起的skb->len长度的数据写入db->io_data地址中,即将数据移入TX SRAM中

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

 

db->tx_pkt_cnt++; //待发送的数据包个数加1

/* TX control: First packet immediately send, second packet queue */

if (db->tx_pkt_cnt == 1) { //如果此数据包是第一个,则直接发送

dm9000_send_packet(dev, skb->ip_summed, skb->len); // 发送函数

} else { //如果是第二个数据包,则需要等待

/* Second packet */

db->queue_pkt_len = skb->len;

db->queue_ip_summed = skb->ip_summed;

netif_stop_queue(dev);

}

 

spin_unlock_irqrestore(&db->lock, flags); //发送完毕后开中断

 

/* free this SKB */

dev_kfree_skb(skb); //释放SKB结构体

 

return NETDEV_TX_OK;

}

 

上层网络协议调用dm9000_start_xmit()函数发送数据.这里说明一下DM9000网卡数据发送的过程:先将数据(skb->data)写入到DM9000内部的TX SRAM中,TX SRAM的容量有限,最多只能容纳两个数据包的数据,分别标记为packet I和packet II.再将packet I的数据长度写入DM9000_TXPLL和DM9000_TXPLH寄存器中,最后将寄存器DM9000_TCR的TCR_TXREQ位置1,packet I将被发送出去.数据发送完毕后,TCR_TXREQ位将被清零.所以dm9000_send_packet()函数的实现过程为:

 

static void dm9000_send_packet(struct net_device *dev,int ip_summed,u16 pkt_len)

{

board_info_t *dm = to_dm9000_board(dev);

 

/* The DM9000 is not smart enough to leave fragmented packets alone. */

if (dm->ip_summed != ip_summed) {

if (ip_summed == CHECKSUM_NONE)

iow(dm, DM9000_TCCR, 0);

else

iow(dm, DM9000_TCCR, TCCR_IP | TCCR_UDP | TCCR_TCP);

dm->ip_summed = ip_summed;

}

 

/* Set TX length to DM9000 */

iow(dm, DM9000_TXPLL, pkt_len);

iow(dm, DM9000_TXPLH, pkt_len >> 8);

 

/* Issue TX polling command */

iow(dm, DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */

}

 

数据发送完毕后,会产生中断,在中断处理例程中会判断中断的类型,如果是发送数据完毕产生的中断,则跳入dm9000_tx_done()函数中,再查看TX SRAM中是否还有未发送的数据包,若没有数据包发送了,则简单标记状态为数据包发送完毕.若有,则继续调用m9000_send_packet()函数将第2个数据包也发送出去,完毕后同样会进入发送完毕中断,再执行上述相同的过程.

dm9000_tx_done()函数实现如下:

 

static void dm9000_tx_done(struct net_device *dev, board_info_t *db)

{

int tx_status = ior(db, DM9000_NSR); /* Got TX status */

 

if (tx_status & (NSR_TX2END | NSR_TX1END)) { //读取寄存器中的值,判断发送状态

/* One packet sent complete */

db->tx_pkt_cnt--;

dev->stats.tx_packets++; //发送完毕后记数

 

if (netif_msg_tx_done(db))

dev_dbg(db->dev, "tx done, NSR %02x/n", tx_status);

 

/* Queue packet check & send */

if (db->tx_pkt_cnt > 0)

dm9000_send_packet(dev, db->queue_ip_summed,db->queue_pkt_len); //继续发送Packet II

 

netif_wake_queue(dev); //启动发送队列

}

}

 

至此,数据发送部分就分析完毕.

 

//数据接收

 

DM9000数据接收可以采取两种方式,查询和中断.linux采取的是后一种.数据的到来相对于CPU来说是异步的.数据接收完毕后,就通过EINT7引脚向处理器报告中断,在中断例程中判断是哪一种中断(数据发送完毕或是数据接收完毕),以此采取不同的动作.中断例程如下:

 

static irqreturn_t dm9000_interrupt(int irq, void *dev_id)

{

struct net_device *dev = dev_id;

board_info_t *db = netdev_priv(dev);

int int_status;

unsigned long flags;

u8 reg_save;

 

dm9000_dbg(db, 3, "entering %s/n", __func__);

 

/* A real interrupt coming */

 

/* holders of db->lock must always block IRQs */

spin_lock_irqsave(&db->lock, flags);

 

/* Save previous register address */

reg_save = readb(db->io_addr); //保存中断前要访问的寄存器的命令

 

/* Disable all interrupts */

iow(db, DM9000_IMR, IMR_PAR); //关闭所有中断

 

/* Got DM9000 interrupt status */

int_status = ior(db, DM9000_ISR); /* Got ISR */ //获取中断状态

iow(db, DM9000_ISR, int_status); /* Clear ISR status */ //清除中断标志

 

if (netif_msg_intr(db))

dev_dbg(db->dev, "interrupt status %02x/n", int_status);

 

/* Received the coming packet */ //数据包的接收

if (int_status & ISR_PRS)

dm9000_rx(dev);

 

/* Trnasmit Interrupt check */

if (int_status & ISR_PTS)

dm9000_tx_done(dev, db); //数据发送完中断,前面已分析


if (db->type != TYPE_DM9000E) {

if (int_status & ISR_LNKCHNG) {

/* fire a link-change request */

schedule_delayed_work(&db->phy_poll, 1);

}

}

 

/* Re-enable interrupt mask */

iow(db, DM9000_IMR, db->imr_all); //所有工作处理完毕,使能中断

 

/* Restore previous register address */

writeb(reg_save, db->io_addr); //将中断之前的要访问寄存器的命令恢复

 

spin_unlock_irqrestore(&db->lock, flags);

 

return IRQ_HANDLED;

}

在中断例程中,通过读取DM9000_ISR寄存器的状态,可以判断中断的类型.若是接收完中断,则跳入dm9000_rx()函数中.其实现如下:

 

static void dm9000_rx(struct net_device *dev)

{

board_info_t *db = netdev_priv(dev);

struct dm9000_rxhdr rxhdr;

struct sk_buff *skb;

u8 rxbyte, *rdptr;

bool GoodPacket; //作为标志,表征数据包的正确与否

int RxLen;

 

/* Check packet ready or not */

do {

ior(db, DM9000_MRCMDX); /* Dummy read */ //读取DM9000_MRCMDX寄存器时,不会改变RX SRAM中的指针值,这里的读取其实 //只是将访问区定位于RX SRAM中

 

/* Get most updated data */

rxbyte = readb(db->io_data); //先读取第一个字节,若正确,应该是01h

 

/* Status check: this byte must be 0 or 1 */

if (rxbyte & DM9000_PKT_ERR) { //接收错误

dev_warn(db->dev, "status check fail: %d/n", rxbyte);

iow(db, DM9000_RCR, 0x00); /* Stop Device */

iow(db, DM9000_ISR, IMR_PAR); /* Stop INT request */

return;

}

 

if (!(rxbyte & DM9000_PKT_RDY)) //接收到的第一个字节与预定不符,退出

return;

 

/* A packet ready now  & Get status/length */

GoodPacket = true;

writeb(DM9000_MRCMD, db->io_addr); //读取DM9000_MRCMD寄存器会改变RX SRAM中的数据指针值

 

(db->inblk)(db->io_data, &rxhdr, sizeof(rxhdr)); //先读取4个字节的头部到 struct dm9000_rxhdr中

 

RxLen = le16_to_cpu(rxhdr.RxLen); //从4字节头部的后2个字节获取数据包的长度

 

if (netif_msg_rx_status(db))

dev_dbg(db->dev, "RX: status %02x, length %04x/n",

rxhdr.RxStatus, RxLen);

 

/* Packet Status check */

if (RxLen < 0x40) { //以太网协议要求数据包的最低长度为64个字节,(以太网帧首部14字节(6+6+2)+最低46字节的数据+4个字节的

                              //CRC校验码),不满足则错误

GoodPacket = false;

if (netif_msg_rx_err(db))

dev_dbg(db->dev, "RX: Bad Packet (runt)/n");

}

 

if (RxLen > DM9000_PKT_MAX) { //若大于数据包的最大长度,出错

dev_dbg(db->dev, "RST: RX Len:%x/n", RxLen);

}

 

/* rxhdr.RxStatus is identical to RSR register. */

if (rxhdr.RxStatus & (RSR_FOE | RSR_CE | RSR_AE | //对各种错误进行统计,记录

     RSR_PLE | RSR_RWTO |

     RSR_LCS | RSR_RF)) {

GoodPacket = false;

if (rxhdr.RxStatus & RSR_FOE) { //FIFO错误

if (netif_msg_rx_err(db))

dev_dbg(db->dev, "fifo error/n");

dev->stats.rx_fifo_errors++;

}

if (rxhdr.RxStatus & RSR_CE) { //校验错误

if (netif_msg_rx_err(db))

dev_dbg(db->dev, "crc error/n");

dev->stats.rx_crc_errors++;

}

if (rxhdr.RxStatus & RSR_RF) { //长度错误

if (netif_msg_rx_err(db))

dev_dbg(db->dev, "length error/n");

dev->stats.rx_length_errors++;

}

}

 

/* Move data from DM9000 */

if (GoodPacket

   && ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) { //数据包正确接收,分配SKB结构用于向上层协议层传递数据

skb_reserve(skb, 2);

rdptr = (u8 *) skb_put(skb, RxLen - 4);

 

/* Read received packet from RX SRAM */

 

(db->inblk)(db->io_data, rdptr, RxLen); //从RX SRAM中将长度为RxLen的数据提取出来,并填充到skb->data结构中去

dev->stats.rx_bytes += RxLen; //记录收到的数据长度(字节)

 

/* Pass to upper layer */

skb->protocol = eth_type_trans(skb, dev); //获取上层协议

if (db->rx_csum) {

if ((((rxbyte & 0x1c) << 3) & rxbyte) == 0)

skb->ip_summed = CHECKSUM_UNNECESSARY;

else

skb->ip_summed = CHECKSUM_NONE;

}

netif_rx(skb); //向上层提交数据

dev->stats.rx_packets++; //记录收到的数据包个数

 

} else {

/* need to dump the packet's data */

 

(db->dumpblk)(db->io_data, RxLen); //数据不正确,丢弃

}

} while (rxbyte & DM9000_PKT_RDY);

}

 

网卡正确接收到数据之后,将其填入skb结构体的数据区,然后通过函数netif_rx(skb)提交到上层协议处理.这里说明一下DM9000网卡的数据处理过程:接收到的数据包都存储在RX SRAM中(复位后为0C00h),每个数据包都有4个字节的头部,分别是1h,status,BYTE_COUNT低8位,BYTE_COUNT高8位.4字节之后是接收到的数据以及CRC校验码.4字节的头部都检验无误之后方能将此数据包读取到skb结构的数据区并提交到上层协议栈.

 

至此,DM9000网卡的驱动就大致分析完毕.

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值