cs8900网卡驱动解析(二)

cs8900_readcs8900_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

 

大神一句话总结:readwordwriteword函数用来读写设备。

 

我们最初分析的函数是cs8900_init,并由此展开,跟踪到了cs8900_probecs8900_readcs8900_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函数,用来完成资源申请和开启网络接口的数据队列任务。

你可能感兴趣的:(cs8900网卡驱动解析(二))