上回留下了cs8900_read和cs8900_write没有讲。本节会专门讲解这两个函数,以及相关联的内容。其实就函数本身并不难,说白了就是向硬件进行读写,完成硬件的控制。但是这个过程是整个驱动程序设计中最关键的。换句话说,你要写驱动,很重要的一部分工作就是设置你的网络芯片,使它能乖乖的为你工作。
好,先看函数原型,它们都在cs8900.c文件中定义:
static inline u16 cs8900_read (struct net_device *dev,u16 reg)
{
outw (reg,dev->base_addr + PP_Address);
return (inw (dev->base_addr + PP_Data));
}
static inline void cs8900_write (struct net_device *dev,u16 reg,u16 value)
{
outw (reg,dev->base_addr + PP_Address);
outw (value,dev->base_addr + PP_Data);
}
看起来真的很简单。reg表示寄存器(不知道寄存器?赶快google去),value表示要赋的值。
/*******
几乎每一种外设都是通过读写设备上的寄存器来进行的。外设寄存器也称为“I/O端口”,通常包括:控制寄存器、状态寄存器和数据寄存器三大类,而且一个外设的寄存器通常被连续地编址。CPU对外设IO端口物理地址的编址方式有两种:一种是I/O映射方式(I/O-mapped),另一种是内存映射方式(Memory-mapped)。而具体采用哪一种则取决于CPU的体系结构。
********/
打开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驱动中使用了就是类似0x0a之类的实地址,而不是我讲的映射后的虚拟地址。其实是他忘记了代码中是使用dev->base_addr+偏移的形式。所以不要再犯同样的错误哦。
剩下的一点知识就是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