cs8900网卡驱动简要解析

和所有其他模块一样,函数init_module是cs8900的入口:

    

cs8900_init函数填充net_device结构体。net_device是网络驱动中最为重要的一个结构,需要认真阅读,其原型在 include/linux/netdevice.h中。为了填充如此复杂的结构,内核提供了ether_setup函数作为辅助。关于ether_setup函数,你只要知道它的功能就可以了。当然,ether_setup不是万能的,你还要手工完成类似如下一些内容: 

   

而下面语句的含义也非常明确:

    

cs8900_probe是初始化函数(Driver initialization routines),主要完成一些初始化操作,我们在下一章中介绍。cs8900_init中使用了另外一个函数还没有说-->alloc_etherdev。alloc_etherdev是alloc_netdev的封装函数,它负责在内核空间为 net_device结构体分配内存(kmalloc)。alloc_etherdev的原型在include/linux/etherdevice.h中。

在函数最后,通过register_netdev完成设备注册。register_netdev函数原型在net/core/dev.c中,但是如果你就是为了写驱动,你可以不往下看了。

一句话总结:cs8900_init在使用各种手段填充了net_device结构体后,通过register_netdev向系统注册了一个网络设备。

cs8900_probe函数本身并不难,但是你必须清楚probe函数的重要职能。因为你很有可能要为属于你的设备编写一个probe函数。

先来看一行最易懂的代码:             printk(VERSION_STRING"/n"); 显然,内核启动时显示的相关信息就是这句话打印出来的。

net_device中的dev_addr就是网卡的MAC地址,你应该提供:

#if defined(CONFIG_ARCH_SMDK2410)  dev->dev_addr[0] = 0x00;  dev->dev_addr[1] = 0x00;  dev->dev_addr[2] = 0x3e;  dev->dev_addr[3] = 0x26;  dev->dev_addr[4] = 0x0a;  dev->dev_addr[5] = 0x00; #endif

使用类似的代码,为我们的网络芯片提供  dev->if_port   = IF_PORT_10BASET; (支持的多种接口)  dev->priv      = (void *) &priv; (设备私有数据结构)

哦,还要多说一句,IF_PORT_10BASET在include/linux/netdevice.h中定义,你通过这个枚举类型,可以了解更多的接口信息:

enum {         IF_PORT_UNKNOWN = 0,         IF_PORT_10BASE2,         IF_PORT_10BASET,         IF_PORT_AUI,         IF_PORT_100BASET,         IF_PORT_100BASETX,         IF_PORT_100BASEFX };

下面的代码对于移植是非常关键的。vSMDK2410_ETH_IO是网卡的虚拟地址,IRQ_EINT9是硬件中断。这两个值和另外一个地址(网卡的物理地址)必须在头文件中定义,而且要正确的定义。

#if defined(CONFIG_ARCH_SMDK2410)     dev->base_addr = vSMDK2410_ETH_IO + 0x300;     dev->irq = IRQ_EINT9; #endif /* #if defined(CONFIG_ARCH_SMDK2410) */

接下来分配I/O端口资源。使用的函数是check_mem_region和request_mem_region。LDD中的理论结合这里的实践,相信你很快就掌握驱动编程中的这个API了。

下面的代码实现了硬件的读写。使用的函数是cs8900_read和cs8900_write。虽然只需很短的篇幅就可以解释这对读写函数,但是为了不分散你对cs8900_probe函数的注意力,我还是留到下一节中说,不要着急:) 哦,差点忘了,这里使用了内核的自旋锁(spin_lock_init),不过关于自旋锁的机制已经超出本文的范围了。你现在只需要知道自旋锁用于并发控制,以及如何动态初始化自旋锁就可以了。如果你还想继续了解,请自行学习与自旋锁相关的API,它们是:

spinlock_t spin; //定义自旋锁 spin_lock(lock); //获得自旋锁 spin_trylock(lock); //尝试获得自旋锁 spin_unlock(lock); //释放自旋锁

一句话总结:cs8900_probe函数继续为设备进行初始化,并申请各种资源。

 

 

 

 

 

 

return (inw (dev->base_addr + PP_Data));

 

 

 

 

 

outw (value,dev->base_addr + PP_Data);

 

 

 

 

 

 

 

#define PP_Data0x0c/* PacketPage Data Port (Section 4.10.10) */

 

 

 

 

 

 

 

 

 

 

cpu_to_le32(v),__io(p))

 

 

#define inw(p)({ __u16 __v = le16_to_cpu((__force __le16) /

 

 

__raw_readl(__io(p))); __v; })

 

#define outsb(p,d,l)__raw_writesb(__io(p),d,l)

 

 

 

 

 

#define insl(p,d,l)__raw_readsl(__io(p),d,l)

 

 

 


 

 

 

ndev->open= cs8900_start;

 

 

cs8900_set (dev,PP_RxCTL,RxOKA | IndividualA | BroadcastA);

cs8900_set (dev,PP_TxCFG,TxOKiE | Out_of_windowiE | JabberiE);

cs8900_set (dev,PP_BufCFG,Rdy4TxiE | RxMissiE | TxUnderruniE | TxColOvfiE | MissOvfloiE);

cs8900_set (dev,PP_LineCTL,SerRxON | SerTxON);

cs8900_set (dev,PP_BusCTL,EnableRQ);

 

 

 

 

 

 

 

 

clear_bit(__LINK_STATE_XOFF, &dev->state);

 

 

 

 

if (netpoll_trap()) {

clear_bit(__LINK_STATE_XOFF, &dev->state);

return;

 

 

if (test_and_clear_bit(__LINK_STATE_XOFF, &dev->state))

__netif_schedule(dev);

 


 

 

 

 

 

 

2.

 

 

 

 

 

 

 

case RxEvent:

 

 

 

case TxEvent:

 

 

 

 

 

 

 

 

 

 

 

 

 

case BufEvent:

 

if ((RegContent (status) & RxMiss)) {

 

priv->stats.rx_errors += missed;

priv->stats.rx_missed_errors += missed;

 

 

if ((RegContent (status) & TxUnderrun)) {

priv->stats.tx_errors++;

priv->stats.tx_fifo_errors++;

 

priv->txlen = 0;

netif_wake_queue (dev);

 

/* FIXME: if Rdy4Tx, transmit last sent packet (if any) */

 

 

case TxCOL:

 

 

 

case RxMISS:

status = MissCount (cs8900_read (dev,PP_RxMISS));

priv->stats.rx_errors += status;

priv->stats.rx_missed_errors += status;

 

 

 

 

 

一句话总结:中断处理函数是中断产生时执行的函数,它根据中断种类进行处理。

 

 

 

 

 

/* These two members must be first. */

struct sk_buff*next;

struct sk_buff*prev;

 

struct sock*sk;

ktime_ttstamp;

struct net_device*dev;

 

structdst_entry*dst;

structsec_path*sp;

 

/*

....................................................................

* layer. Please put your private variables there. If you

* want to keep them across layers you have to do a skb_clone()

* first. This is owned by whoever has the skb queued ATM.

*/

charcb[48];

 

unsigned intlen,

data_len;

__u16mac_len,

hdr_len;

union {

__wsumcsum;

struct {

__u16csum_start;

__u16csum_offset;

 

 

__u32priority;

__u8local_df:1,

cloned:1,

ip_summed:2,

nohdr:1,

nfctinfo:3;

__u8pkt_type:3,

fclone:2,

ipvs_property:1,

peeked:1,

nf_trace:1;

__be16protocol;

 

void(*destructor)(struct sk_buff *skb);

 

struct nf_conntrack*nfct;

struct sk_buff*nfct_reasm;

 

 

struct nf_bridge_info*nf_bridge;

 

 

intiif;

 

__u16queue_mapping;

 

 

__u16tc_index;/* traffic control index */

 

__u16tc_verd;/* traffic control verdict */

 

 

/* 2 byte hole */

 

 

dma_cookie_tdma_cookie;

 

 

__u32secmark;

 

 

__u32mark;

 

sk_buff_data_ttransport_header;

sk_buff_data_tnetwork_header;

sk_buff_data_tmac_header;

/* These elements must be at the end, see alloc_skb() for details.*/

sk_buff_data_ttail;

sk_buff_data_tend;

unsigned char*head,

*data;

unsigned inttruesize;

atomic_tusers;

 

 

 

 

 

 

 

 

 

 

NET_RX_SUCCESS(no congestion)

NET_RX_DROP(packet was dropped)

 

 

本文固定链接:http://www.verydemo.com/demo_c167_i122021.html

猜你喜欢

转载自jcdnwz00930.iteye.com/blog/2072663