cs8900_read和cs8900_write这两个函数本身的结构不是很难。
说白了就是向硬件进行读写,完成硬件的控制。但是这个过程是整个驱动程序设计中最关键的。换句话说,你要写驱动,很重要的一部分工作就是设置你的网络芯片,使它能乖乖的为你工作。
函数原型定义如下:
//这是一个纯正的系统调用。读取寄存器的值并返回。
//这一部分也是纯正的底层的硬件操作。
inline int readword(struct net_device *dev, int portno) {
return inw(dev->base_addr + portno);
}
inline void writeword(struct net_device *dev, int portno, int value) {
outw(value, dev->base_addr + portno);
}
关于寄存器的地址定义那么就是裸机的问题了:
打开cs8900.h和硬件芯片的datasheet,你就会明白了:
#define PP_Address 0x0a /* PacketPage Pointer Port (Section 4.10.10) */
#define PP_Data 0x0c /* PacketPage Data Port (Section 4.10.10) */
剩下的一点知识就是Linux提供的I/O端口访问方法。除了本例使用的outw/inw外,还有下面几个(linux/include/asm-arm/io.h)。它们的区别我记得在LDD中有了解释。
#ifdef __io
#define outb(v,p) __raw_writeb(v,__io(p))
#define outw(v,p) __raw_writew((__force __u16) /
cpu_to_le16(v),__io(p))
#define outl(v,p) __raw_writel((__force __u32) /
cpu_to_le32(v),__io(p))
#define inb(p) ({ __u8 __v = __raw_readb(__io(p)); __v; })
#define inw(p) ({ __u16 __v = le16_to_cpu((__force __le16) /
__raw_readw(__io(p))); __v; })
#define inl(p) ({ __u32 __v = le32_to_cpu((__force __le32) /
__raw_readl(__io(p))); __v; })
#define outsb(p,d,l) __raw_writesb(__io(p),d,l)
#define outsw(p,d,l) __raw_writesw(__io(p),d,l)
#define outsl(p,d,l) __raw_writesl(__io(p),d,l)
#define insb(p,d,l) __raw_readsb(__io(p),d,l)
#define insw(p,d,l) __raw_readsw(__io(p),d,l)
#define insl(p,d,l) __raw_readsl(__io(p),d,l)
#endif
大神一句话总结:readword和writeword函数用来读写设备。
我们最初分析的函数是cs8900_init,并由此展开,跟踪到了cs8900_probe、cs8900_read、cs8900_write等,现在收回来,还是回到cs8900_init中。
在probe1找到一行代码:dev->open = net_open;很容易联想到file_operation中的open。其实它们是一样的。open函数在网络设备被激活时(ifconfig)调用。因此,我们在编写网卡驱动时,要考虑网卡激活时,需要完成哪些事情。通常要进行中断的申请、资源的申请等。在cs8900的驱动中,主要完成两件事情:激活网卡和申请中断。
下面是网卡激活代码的实现:
//下面的代码开始了一系列网卡激活代码的实现。
/* Turn on both receive and transmit operations */
writereg(dev, PP_LineCTL,
readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON);
/* Receive only error free packets addressed to this card */
lp->rx_mode = 0;
writereg(dev, PP_RxCTL, DEF_RX_ACCEPT);
lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL;
if (lp->isa_config & STREAM_TRANSFER)
lp->curr_rx_cfg |= RX_STREAM_ENBL;
writereg(dev, PP_RxCFG, lp->curr_rx_cfg);
writereg(dev, PP_TxCFG,
TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL |
TX_LATE_COL_ENBL | TX_JBR_ENBL |
TX_ANY_COL_ENBL | TX_16_COL_ENBL);
writereg(dev, PP_BufCFG,
READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL |
TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL);
/* now that we've got our act together, enable everything */
writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) | ENABLE_IRQ);
//使能中断。
enable_irq(dev->irq);
显然,我们下一个任务就是完成中断处理函数了(cs8900_interrupt),我们后面再说。
当资源准备就绪后,需要调用netif_start_queue函数开启网络接口的接收和发送数据队列。这个函数原型在netdevivce.h中。与它类似的还有一个函数,叫netif_wake_queue。有人更喜欢使用netif_wake_queue函数,因为它可以通知网络系统可再次开始传输数据包。
这个函数的实现我手上的程序是在内核中实现的:
static inline void netif_start_queue(struct net_device *dev)
{
clear_bit(__LINK_STATE_XOFF, &dev->state);
}
static inline void netif_wake_queue(struct net_device *dev)
{
#ifdef CONFIG_NETPOLL_TRAP
if (netpoll_trap()) {
clear_bit(__LINK_STATE_XOFF, &dev->state);
return;
}
#endif
if (test_and_clear_bit(__LINK_STATE_XOFF, &dev->state))
__netif_schedule(dev);
}
大神一句话总结:我们需要实现一个open函数,用来完成资源申请和开启网络接口的数据队列任务。