一步步写网卡驱动(三)
来源:互联网 发布:mac谷歌浏览器打不开 编辑:程序博客网 时间:2024/05/22 04:39
昨天说到DM9000的open函数, 我们先来分析下网卡的open函数需要完成什么功能:
1. 注册中断处理例程
2. 初始化网卡
3. 使能发送队列
static int lydm9k_open(struct net_device *ndev){ struct lydm9k_priv *priv = netdev_priv(ndev); unsigned long irqflags = priv->irq_res->flags & IRQF_TRIGGER_MASK; printdbg("invoked\n"); irqflags |= IRQF_SHARED; if(request_irq(ndev->irq, dm9k_interrupt, irqflags, ndev->name, ndev)) // 注册网卡中断处理例程 { dm9kmsg("request irq fail\n"); return -EAGAIN; } // 初始化网卡 dm9k_reset(priv); dm9k_init(priv); ndev->trans_start = 0; netif_start_queue(ndev); // 使能发送队列 return 0;}
先来看下网卡的初始化, 这部分都是硬件寄存器操作, 一两句也说不清楚, 大家对DM9000感兴趣的可以看下DM9000的datasheet, 只是想了解网卡驱动模型的只要知道这段代码作用就可以了:
static void inline dm9k_reset(struct lydm9k_priv *priv){ regw(priv, DM9000_NCR, NCR_RST | (1<<1)); udelay(200); // at least 20 usecs}static void inline dm9k_init(struct lydm9k_priv *priv){ int i; int oft; regw(priv, DM9000_GPCR, GPCR_GEP_CNTL); regw(priv, DM9000_GPR, 0); regw(priv, DM9000_RCR, RCR_RXEN | RCR_DIS_CRC | RCR_DIS_LONG); regw(priv, DM9000_TCR, 0x00); regw(priv, DM9000_BPTR, 0x3f); regw(priv, DM9000_FCTR, 0x38); regw(priv, DM9000_FCR, 0xff); regw(priv, DM9000_SMCR, 0x00); regw(priv, DM9000_NSR, NSR_TX1END | NSR_TX2END | NSR_WAKEST); regw(priv, DM9000_ISR, 0x0f); // clear interrupt status regw(priv, DM9000_PAB0, priv->mac[0]); regw(priv, DM9000_PAB1, priv->mac[1]); regw(priv, DM9000_PAB2, priv->mac[2]); regw(priv, DM9000_PAB3, priv->mac[3]); regw(priv, DM9000_PAB4, priv->mac[4]); regw(priv, DM9000_PAB5, priv->mac[5]); for(i = 0, oft = DM9000_MAR; i<8; i++, oft++) { regw(priv, oft, 0xff); } regw(priv, DM9000_IMR, IMR_PRM | IMR_PTM | IMR_PAR);}
接着来看中断处理例程, 从我们对IMR寄存器的设置看出我们需要关心的有网卡的接收中断和发送完成中断
static irqreturn_t dm9k_interrupt(int irq, void *dev_id){ struct net_device *ndev = dev_id; struct lydm9k_priv *priv = netdev_priv(ndev); unsigned char int_status; unsigned char reg_save; /* Save previous register address */ reg_save = readb(priv->io_reg); /* Disable all interrupts */ regw(priv, DM9000_IMR, IMR_PAR); int_status = regr(priv, DM9000_ISR); regw(priv, DM9000_ISR, int_status); printdbg("int_status = %02x\n", int_status); // recv irq if(int_status & ISR_PRS) { while(0 == dm9k_recv_pkt(ndev)); } // trans irq if(int_status & ISR_PTS) { dm9k_tx_done(ndev); } /* Re-enable interrupt mask */ regw(priv, DM9000_IMR, IMR_PRM | IMR_PTM | IMR_PAR); /* Restore previous register address */ writeb(reg_save, priv->io_reg); return IRQ_HANDLED;}
我们先来分析接收中断, 当网卡接收到数据包之后, 我们需要从网卡里吧数据包读出来, 并构造一个skb用来描述接收到的数据, 并将skb传输给网络协议栈:
struct dm9000_rxhdr { u8 RxPktReady; u8 RxStatus; __le16 RxLen;} __attribute__((__packed__));
static int dm9k_recv_pkt(struct net_device *ndev){ unsigned char rx_status; unsigned short rx_len; struct dm9000_rxhdr rxhdr; unsigned char *data; struct sk_buff *skb; struct lydm9k_priv *priv = netdev_priv(ndev);
printdbg("invoked\n");
/* Get most updated data */ rx_status = regr(priv, DM9000_MRCMDX); printdbg("rx_status = 0x%x\n", rx_status);
/* Status check: this byte must be 0 or 1 */ if (rx_status & DM9000_PKT_ERR) { printdbg("recv pkt err\n"); regw(priv, DM9000_RCR, 0x00); /* Stop Device */ regw(priv, DM9000_ISR, IMR_PAR); /* Stop INT request */ return -1; }
if (!(rx_status & DM9000_PKT_RDY)) { printdbg("there is no pkt\n"); return -1; }
inblk(priv, DM9000_MRCMD, &rxhdr, sizeof(rxhdr)); rx_len = le16_to_cpu(rxhdr.RxLen); printdbg("recv 0x%x bytes\n", rx_len);
skb = dev_alloc_skb(rx_len + 4); if(NULL == skb) { dumpblk(priv, DM9000_MRCMD, rx_len); return -ENOMEM; }
skb_reserve(skb, 2); data = skb_put(skb, rx_len - 4); inblk(priv, DM9000_MRCMD, data, rx_len);
skb->protocol = eth_type_trans(skb, ndev); skb->ip_summed = CHECKSUM_NONE;
#ifdef LYDM9000_DEBUG for(i = 0; i<skb->len; i++) { printk("0x%02x\t", *(skb->data + i)); } printk("\n");#endif
netif_rx(skb);
return 0;
return -1;}
然后我们来分析数据包的发送, 当上层需要发送一个数据包时就会调用ndo_start_xmit将要发送的skb传送到网卡驱动, 我们需要在驱动程序中实现将skb中的数据拷贝到网卡的发送缓冲区里, 并发送, 在发送完成之前禁止下个数据包请求ndo_start_xmit发送(其实DM9000的发送缓冲区空间足够容纳2个数据包, 我们为了简单, 之实现发送一个skb)
static int lydm9k_start_xmit(struct sk_buff *skb, struct net_device *ndev){ struct lydm9k_priv *priv = netdev_priv(ndev); printdbg("invoked\n"); dm9k_send_pkt(priv, skb->data, skb->len); netif_stop_queue(ndev); dev_kfree_skb(skb); return NETDEV_TX_OK;}
static void dm9k_send_pkt(struct lydm9k_priv *priv, unsigned char *data, size_t len){ regw(priv, DM9000_TXPLH, (len >> 8) & 0xff); regw(priv, DM9000_TXPLL, len & 0xff);
outblk(priv, DM9000_MWCMD, data, len);
regw(priv, DM9000_TCR, TCR_TXREQ);}
当数据包发送完成就会触发发送完成中断, 在中断处理例程里可以看到, 此时我们需要使能并唤醒发送队列, 发送下个数据包
static void dm9k_tx_done(struct net_device *ndev){ struct lydm9k_priv *priv = netdev_priv(ndev); unsigned char tx_status = regr(priv, DM9000_NSR); if(tx_status & (NSR_TX1END | NSR_TX2END)) { netif_wake_queue(ndev); }}
天有不测风云, 数据包可能会由于某种原因, 未能及时发送出去或发送完成中断丢失, 此时驱动程序的ndo_tx_timeout函数就会被调用, 此时我们需要做的是重置网卡并使能发送队列
static void lydm9k_timeout(struct net_device *ndev){ struct lydm9k_priv *priv = netdev_priv(ndev); printdbg("invoked\n"); netif_stop_queue(ndev); dm9k_reset(priv); dm9k_init(priv); ndev->trans_start = jiffies; netif_start_queue(ndev);}
OK, 到此我们的网卡应该就能正常的发送和接收数据了, 其他的一些相关细节, 我们以后有时间在慢慢完善, 现在就去试试我们自己写的网卡驱动吧!
- 一步步写网卡驱动(三)
- 一步步写网卡驱动(-)
- 一步步写网卡驱动(二)
- 网卡驱动(三)
- 一步步写驱动--模块
- 一步步写驱动---cdev
- 一步步写水面渲染(三)
- 一步步写驱动--module_init/module_exit
- 一步步写驱动--设备号
- cs8900网卡驱动解析(三)
- mini2440 dm9000 网卡驱动详解 (三)
- dm9000网卡驱动分析(三)
- ARM-Linux驱动--DM9000网卡驱动分析(三)
- ARM-Linux驱动--DM9000网卡驱动分析(三) .
- ARM-Linux驱动--DM9000网卡驱动分析(三)
- ARM-Linux驱动--DM9000网卡驱动分析(三)
- 一步步讲解如何调试vxworks网卡驱动-适合嵌入式初学者
- Linux 网卡驱动学习(三)(net_device 等数据结构)
- HTML--小知识
- 【第二章】 IoC 之 2.3 IoC的配置使用——跟我学Spring3
- 统计Java项目代码行数
- 用IrisSkin美化WinForm界面
- .NET线程控制
- 一步步写网卡驱动(三)
- C语言-获取程序运行的当前目录函数GetCurrentDirectory
- 内存分配(一)堆和栈的区别
- poj Dollar Dayz(完全背包)
- java 内部类
- OpenCV 单通道三通道理解
- .obj文件和.exe文件
- IOS后台运行机制详解(一)
- CMD文件的原理