Rtl8019 for S3C44B0 驱动程序源码解析
时间:2010-04-25
来源:lixin198705
在手机上看
手机扫描阅读
Rylex 发布于:2008-08-20 09:20
//////////////// I.与网络设备相关的宏: /////////////////////////////////
/*下列宏定义于<linux/if_ether.h>中
ETH_ALEN 网络设备的硬件地址(即MAC地址)长度,单位为一个八位字节,该值通常为6。
ETH_DATA_LEN 每次传输的最大数据量(不包括14位设备报头)。该值通常为1500。
ETH_HLEN 设备报头长度。该值通常为14。(包括目的地址(DA长度6)、源地址(SA长度6)、类型字段(TYPE长度2))
ETH_ZLEN 整个数据包的最小长度。该值通常为60。
*/
/*MAX_ADDR_LEN 该宏定义于<linux/netdevice.h>中,指的是网络设备地址(即MAC地址)的最大长度,该值通常为32。*/
/////////////// II.与网络设备相关的结构体: //////////////////////////////////
/********************************* struct net_device *************************************************/
/*为了屏蔽网络环境中物理网络设备的多样性,Linux对所有的物理网络设备进行抽象并定义了一个统一的概念,
称之为接口(Interface)。每个接口在内部都表现为一个这样的结构体。以下为该结构体的定义:*/
struct net_device
{
/* 以下为该结构体的可见成员,用户可从Space.c等文件中查看到它们*/
char name[IFNAMSIZ]; /* 网络设备的名称*/
unsigned long mem_end; /* 设备发送和接收数据时需要占用内存空间,该值为内存区域的结束地址*/
unsigned long mem_start; /* 共享内存区域的起始地址*/
unsigned long base_addr; /* 设备I/O操作的基地址,由驱动程序设置,该成员可通过ifconfig来查看或更新*/
unsigned int irq; /* 设备的中断号,该成员可通过ifconfig来查看或更新*/
/*
* 下列成员只用于部分设备,它们不会出现在Space.c文件中
*/
unsigned char if_port; /* 端口传输特性,用于多端口设备,可选值如下:
enum {
IF_PORT_UNKNOWN = 0, //未知方式
IF_PORT_10BASE2, //10兆同轴方式
IF_PORT_10BASET, //10兆双绞方式
IF_PORT_AUI,
IF_PORT_100BASET,
IF_PORT_100BASETX,
IF_PORT_100BASEFX
};*/
unsigned char dma; /* 设备的DMA通道,通常只在与外部总线通讯时才有意义,通过ifconfig可查看该成员*/
unsigned long state; /* 设备的当前状态*/
struct net_device *next; /* 指向全局设备链表中下一个设备的指针*/
int (*init)(struct net_device *dev); /* 设备的初始化函数,仅需调用一次 */
/* 至此,在Space.c文件中可见的成员介绍完毕 */
//////////////////////////////////////////////////////////////////////////////////////////
struct net_device *next_sched;
/* 设备索引,唯一的设备标识 */
int ifindex;
int iflink;
struct net_device_stats* (*get_stats)(struct net_device *dev);
struct iw_statistics* (*get_wireless_stats)(struct net_device *dev);
/*用于处理无线传输的函数,ioctl的替代品,详情查看 <net/iw_handler.h>*/
struct iw_handler_def * wireless_handlers;
struct ethtool_ops *ethtool_ops;
/* 至此,该结构体的可见成员已介绍完毕,以下的所有成员都属于系统底层,
* 而且可能会有不定期变动。*/
/* 在未来处理网络设备掉电的代码中也许会用到以下成员:*/
unsigned long trans_start; /* 进行最后一次发送操作的时间(以jiffies表示)*/
unsigned long last_rx; /* 进行最后一次接收操作的时间*/
unsigned short flags; /* 设备标记。可用标记查看<linux/if.h>*/
unsigned short gflags; /* 全局标记(?)*/
unsigned short priv_flags; /* 类似于flags,但用户空间不可见 */
unsigned short unused_alignment_fixer; /* Because we need priv_flags,
* and we want to be 32-bit aligned.
*/
unsigned mtu; /* 每次传输的最大数据量(即ETH_DATA_LEN),缺省值为1500字节,该成员可通过ifconfig更改*/
unsigned short type; /* 设备类型。(ARP)地址解析协议根据该成员来判断接口支持何种硬件地址。对于以太网设备
来说,合适的值为ARPHRD_ETHER。在<linux/if_arp.h>中有所有设备类型。该成员通过ether_setup设置*/
unsigned short hard_header_len;/* 设备报头长度。即位于发送数据包中IP报头之前的数据长度(即ETH_HLEN),
以太网设备的该值为14。*/
void *priv; /* 相当于filp->private_data。通过alloc_netdev设置,通过netdev_priv访问*/
struct net_device *master; /* 该指针指向当前设备组中该设备所属的主设备*/
/* 设备地址相关信息 */
unsigned char broadcast[MAX_ADDR_LEN]; /* 广播地址。由ether_setup 进行分配*/
unsigned char dev_addr[MAX_ADDR_LEN]; /* 设备地址(MAC地址),通过读取设备信息获得*/
unsigned char addr_len; /* 设备地址长度(MAC地址),通常为6*/
struct dev_mc_list *mc_list; /* 用于多点传送的MAC地址列表 */
int mc_count; /* 多点传送时MAC地址列表中的元素个数 */
int promiscuity;
int allmulti;
int watchdog_timeo; /* 该成员是一个以jiffies为单位的时间数。表示的是网络层判定传输超时的最小时间标准*/
struct timer_list watchdog_timer; /* 用来为超时计数的定时器(?)*/
/* Protocol specific pointers */
void *atalk_ptr; /* AppleTalk link */
void *ip_ptr; /* IPv4 specific data */
void *dn_ptr; /* DECnet specific data */
void *ip6_ptr; /* IPv6 specific data */
void *ec_ptr; /* Econet specific data */
void *ax25_ptr; /* AX.25 specific data */
struct list_head poll_list; /* Link to poll list */
int quota;
int weight;
struct Qdisc *qdisc;
struct Qdisc *qdisc_sleeping;
struct Qdisc *qdisc_ingress;
struct list_head qdisc_list;
unsigned long tx_queue_len; /* Max frames per queue allowed */
/* ingress path synchronizer */
spinlock_t ingress_lock;
/* hard_start_xmit synchronizer */
spinlock_t xmit_lock; /* 该锁用来防止同一时间内对hard_start_xmit函数进行多次调用*/
/* cpu id of processor entered to hard_start_xmit or -1,
if nobody entered there.
*/
int xmit_lock_owner; /* 该成员指的是获得了xmit_lock的CPU的ID号。-1表示没有当前CPU获得xmit_lock*/
/* device queue lock */
spinlock_t queue_lock;
/* Number of references to this device */
atomic_t refcnt;
/* delayed register/unregister */
struct list_head todo_list;
/* device name hash chain */
struct hlist_node name_hlist;
/* device index hash chain */
struct hlist_node index_hlist;
/* register/unregister state machine */
enum { NETREG_UNINITIALIZED=0,
NETREG_REGISTERING, /* called register_netdevice */
NETREG_REGISTERED, /* completed register todo */
NETREG_UNREGISTERING, /* called unregister_netdevice */
NETREG_UNREGISTERED, /* completed unregister todo */
NETREG_RELEASED, /* called free_netdev */
} reg_state;
/* 网络设备的特性 */
int features;
#define NETIF_F_SG 1 /* 分散/整合的I/O操作。标志设备可以将分散的数据包整合后再发送 */
#define NETIF_F_IP_CSUM 2 /* 只对TCP/UDP包进行校验 */
#define NETIF_F_NO_CSUM 4 /* 设备无需进行包校验操作 */
#define NETIF_F_HW_CSUM 8 /* 校验所有类型的数据包 */
#define NETIF_F_HIGHDMA 32 /* 可直接存取内存中的高地址 */
#define NETIF_F_FRAGLIST 64 /* 分散/整合的I/O操作。标志设备可以在接收到分散的数据后将其整合*/
#define NETIF_F_HW_VLAN_TX 128 /* Transmit VLAN hw acceleration */
#define NETIF_F_HW_VLAN_RX 256 /* Receive VLAN hw acceleration */
#define NETIF_F_HW_VLAN_FILTER 512 /* Receive filtering on VLAN */
#define NETIF_F_VLAN_CHALLENGED 1024 /* Device cannot handle VLAN packets */
#define NETIF_F_TSO 2048 /* Can offload TCP/IP segmentation */
#define NETIF_F_LLTX 4096 /* LockLess TX */
/* 当设备与网络断开时调用该函数 */
void (*uninit)(struct net_device *dev);
/* 设备的析构函数,当设备的所有外部引用都消失后调用该函数 */
void (*destructor)(struct net_device *dev);
/********** 设备操作函数指针 ***********/
int (*open)(struct net_device *dev); /*设备的打开函数。当ifconfig激活设备后设备就会被打开,
该函数的工作是注册设备所需的系统资源(I/O端口,中断号及DMA通道等),
打开硬件设备并进行设备所需的其他设置。 */
int (*stop)(struct net_device *dev); /*设备的终止函数。当接口被析构时设备就会被停止。该函数执行与open函数相反的操作。*/
int (*hard_start_xmit) (struct sk_buff *skb, /*发送一个完整的数据包*/
struct net_device *dev);
#define HAVE_NETDEV_POLL
int (*poll) (struct net_device *dev, int *quota); /*由NAPI驱动程序提供的以轮询方式工作的设备操作函数*/
int (*hard_header) (struct sk_buff *skb, /*该函数根据源设备及目标设备的地址组合一个设备报头。*/
struct net_device *dev, /*以太网设备的该函数默认为eth_header。*/
unsigned short type, /*该函数在调用hard_start_xmit之前被调用。*/
void *daddr,
void *saddr,
unsigned len);
int (*rebuild_header)(struct sk_buff *skb); /*该函数负责重建设备报头以完善其信息。
它在地址解析完成后,发送首个数据包之前被调用。*/
#define HAVE_MULTICAST
void (*set_multicast_list)(struct net_device *dev); /*当设备多点传送的地址列表和设备标志被更改时调用该函数。*/
#define HAVE_SET_MAC_ADDR
int (*set_mac_address)(struct net_device *dev, /*在设备支持的情况下设置设备的MAC地址。*/
void *addr);
#define HAVE_PRIVATE_IOCTL
int (*do_ioctl)(struct net_device *dev, /*实现设备特有的ioctl命令。不需要时可将参数1设为NULL。*/
struct ifreq *ifr, int cmd);
#define HAVE_SET_CONFIG
int (*set_config)(struct net_device *dev, /*旧有的更改设备设置的函数。目前的设备不需要该函数。*/
struct ifmap *map);
#define HAVE_HEADER_CACHE
int (*hard_header_cache)(struct neighbour *neigh, /*将地址解析的结果数据填充到hh_cache结构体中。*/
struct hh_cache *hh); /*以太网设备的该函数默认为eth_header_cache 。*/
void (*header_cache_update)(struct hh_cache *hh, /*当地址解析的结果变更时更新hh_cache结构体中的信息。*/
struct net_device *dev, /*以太网设备的默认函数是eth_header_cache_update。*/
unsigned char * haddr);
#define HAVE_CHANGE_MTU
int (*change_mtu)(struct net_device *dev, int new_mtu); /*改变设备单次传输的最大数据量*/
#define HAVE_TX_TIMEOUT
void (*tx_timeout) (struct net_device *dev); /*当一个数据包发送失败时(可能是错过一次中断或设备被锁),
该函数负责处理相应问题并恢复传输。*/
void (*vlan_rx_register)(struct net_device *dev,
struct vlan_group *grp);
void (*vlan_rx_add_vid)(struct net_device *dev,
unsigned short vid);
void (*vlan_rx_kill_vid)(struct net_device *dev,
unsigned short vid);
int (*hard_header_parse)(struct sk_buff *skb, /*该函数从skb包含的数据包中取出源地址,将其复制到haddr指向*/
unsigned char *haddr); /*的缓冲区中,并返回源地址长度。以太网设备的默认函数为eth_header_parse*/
int (*neigh_setup)(struct net_device *dev, struct neigh_parms *);
int (*accept_fastpath)(struct net_device *, struct dst_entry*);
#ifdef CONFIG_NETPOLL
int netpoll_rx;
#endif
#ifdef CONFIG_NET_POLL_CONTROLLER
void (*poll_controller)(struct net_device *dev); /*当禁用中断时,该函数用于请求驱动程序检查设备事件状况。*/
#endif
/* bridge stuff */
struct net_bridge_port *br_port;
#ifdef CONFIG_NET_DIVERT
/* this will get initialized at each interface type init routine?*/
struct divert_blk *divert;
#endif /* CONFIG_NET_DIVERT */
/* class/net/name entry */
struct class_device class_dev;
/* how much padding had been added by alloc_netdev() */
int padded;
};
/********************************** struct net_device_stats ***********************************/
/*该结构体用来描述网络设备进行数据传输的统计信息。以下为该结构体的定义:*/
struct net_device_stats
{
unsigned long rx_packets; /* 已接收到的数据包总数 */
unsigned long tx_packets; /* 已发送的数据包总数 */
unsigned long rx_bytes; /* 已接收到的字节总数 */
unsigned long tx_bytes; /* 已发送的字节总数 */
unsigned long rx_errors; /* 接收失败的数据包总数 */
unsigned long tx_errors; /* 传输失败的数据包总数 */
unsigned long rx_dropped; /* 由于接收缓冲区的空间不足而被丢掉的数据包总数 */
unsigned long tx_dropped; /* 由于发送缓冲区的空间不足而被丢掉的数据包总数 */
unsigned long multicast; /* 多点传送时接收到的数据包总数 */
unsigned long collisions; /* 由于媒介堵塞所引发的冲突次数 */
/* 接收失败的各种情况 */
unsigned long rx_length_errors; /* 接收的数据长度不符 */
unsigned long rx_over_errors; /* 接收缓冲区溢出 */
unsigned long rx_crc_errors; /* CRC校验错误 */
unsigned long rx_frame_errors; /* 帧同步错误 */
unsigned long rx_fifo_errors; /* 先入先出操作超度 */
unsigned long rx_missed_errors; /* 丢包 */
/* 发送失败的各种情况 */
unsigned long tx_aborted_errors;
unsigned long tx_carrier_errors;
unsigned long tx_fifo_errors;
unsigned long tx_heartbeat_errors;
unsigned long tx_window_errors;
/* for cslip etc */
unsigned long rx_compressed;
unsigned long tx_compressed;
};
/********************************** struct sk_buff ***********************************/
struct sk_buff {
/* 这两个成员必须放在最前面 */
struct sk_buff *next;
struct sk_buff *prev;
struct sk_buff_head *list;
struct sock *sk;
struct timeval stamp;
struct net_device *dev; /* 发送或接收该缓冲区的网络设备 */
struct net_device *input_dev;
struct net_device *real_dev;
union {
struct tcphdr *th;
struct udphdr *uh;
struct icmphdr *icmph;
struct igmphdr *igmph;
struct iphdr *ipiph;
struct ipv6hdr *ipv6h;
unsigned char *raw;
} h; /* 指向数据包报头中的传输层报头*/
union {
struct iphdr *iph;
struct ipv6hdr *ipv6h;
struct arphdr *arph;
unsigned char *raw;
} nh; /* 指向数据包报头中的网络层报头*/
union {
unsigned char *raw;
} mac; /* 指向数据包报头中的链路层报头*/
struct dst_entry *dst;
struct sec_path *sp;
/*
* This is the control buffer. It is free to use for every
* 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.
*/
char cb[40];
unsigned int len, /* 数据包中数据总长度 */
data_len, /* 数据包中分段存储的数据长度。除非使用了分散/整合的I/O操作,否则该成员始终为0*/
mac_len, /* MAC地址长度(?) */
csum;
unsigned char local_df,
cloned,
pkt_type, /* 数据包类别。该成员由eth_type_trans 负责设置。常用值包括:
PACKET_HOST (发送给本机的包)
PACKET_OTHERHOST (发送给其他主机的包)
PACKET_BROADCAST (广播发送的包)
PACKET_MULTICAST (多点传送的包)*/
ip_summed; /* 数据包的校验策略 */
__u32 priority;
unsigned short protocol,
security;
void (*destructor)(struct sk_buff *skb);
#ifdef CONFIG_NETFILTER
unsigned long nfmark;
__u32 nfcache;
__u32 nfctinfo;
struct nf_conntrack *nfct;
#ifdef CONFIG_NETFILTER_DEBUG
unsigned int nf_debug;
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
struct nf_bridge_info *nf_bridge;
#endif
#endif /* CONFIG_NETFILTER */
#if defined(CONFIG_HIPPI)
union {
__u32 ifield;
} private;
#endif
#ifdef CONFIG_NET_SCHED
__u32 tc_index; /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
__u32 tc_verd; /* traffic control verdict */
__u32 tc_classid; /* traffic control classid */
#endif
#endif
/* 这些成员必须放在最后,详细原因查看alloc_skb() */
unsigned int truesize;
atomic_t users;
/* 以下成员用于对数据包中的数据进行寻址*/
unsigned char *head, /* 指向所分配空间的起始地址 */
*data, /* 指向有效数据的起始地址 */
*tail, /* 指向有效数据的结束地址 */
*end; /* 指向所分配空间的结束地址 */
};
////////////////////// III.rtl8019.c源码分析 /////////////////////////////////
/******************** 变量、宏、结构体 *******************/
#define outportb(port, data) *((volatile u8 *)(port)) = (u8)(data) /* 8位的端口写入函数 */
#define inportb(port) *((volatile u8 *)(port)) /* 8位的端口读取函数 */
#define outportw(port, data) *((volatile u16 *)(port)) = (u16)(data) /* 16位的端口写入函数 */
#define inportw(port) *((volatile u16 *)(port)) /* 16位的端口读取函数 */
#define ETH_FRAME_LEN 1514 /* 整个封包的最大长度 */
/* Rtl8019的缓冲区范围为0x40~0x80,大小为16k,分为64页,每页大小256b。其中: */
#define RPSTART 0x4c /* Rtl8019的RAM中接收缓冲区的起始页地址 */
#define RPSTOP 0x80 /* Rtl8019的RAM中接收缓冲区的结束页地址 */
#define SPSTART 0x40 /* Rtl8019的RAM中发送缓冲区的起始页地址 */
static int timeout = 100; /* 发送超时 tx watchdog ticks 100 = 1s */
static char *version = "Samsung S3C44B0 Rtl8019as driver version 1.0\n"; /* 版本号 */
static u8 rBNRY; /* 当前读取的页地址 */
static u8 SrcMacID[ETH_ALEN] = {0x00,0x80,0x48,0x12,0x34,0x56}; /* MAC地址 */
/* 设备私有的结构体。用于接收及发送数据包。 */
struct nic_8019_priv {
struct net_device_stats stats; /* 设备的传输统计对象 */
spinlock_t lock; /* 自旋锁 */
struct sk_buff *skb; /* socket缓冲区 */
};
/************************ 操作函数 *******************************/
/***** 选择寄存器页(Rtl8019as共有Page0~3四个寄存器页)*****/
static void SetRegPage( u8 PageIdx)
{
u8 temp;
temp = inportb(BaseAddr); /* 读取Rtl8019第0寄存器页中的CR寄存器值 */
temp = (temp&0x3b)|(PageIdx<<6); /* 将寄存器当前值与上3b得出前6位的无效值。CR寄存器的高两位用于设置页号,
所以需要将页号左移6位再进行或运算 */
outportb(BaseAddr, temp); /* 设置CR寄存器的值 */
}
/***** 设备的初始化函数 *****/
static int nic_8019_init(struct net_device *dev)
{
int i;
TRACE("init\n"); /* 输出跟踪信息 */
ether_setup(dev); /* ether_setup用来设置设备的各个成员 */
// 设置设备的操作函数
dev->open = nic_8019_open; /* 设备的打开函数 */
dev->stop = nic_8019_stop; /* 设备的终止函数 */
dev->get_stats = nic_8019_get_stats; /* 设备的统计函数 */
dev->hard_start_xmit = nic_8019_start_xmit; /* 设备的发送函数 */
// 设置设备的操作参数
dev->watchdog_timeo = timeout; /* 设置超时为timeout(100 ticks=1s) */
dev->irq = S3C44B0X_INTERRUPT_EINT1; /* 设置中断号。S3C44B0X_INTERRUPT_EINT1定义于<include/asm/arch/irqsh.h>,默认值为24 */
dev->dma = 0; /* 设置dma通道 */
// 设置MAC地址
printk(KERN_INFO "%s: ", dev->name); /* 输出设备名称以便明确操作 */
for(i=0; i<6; i++) { /* 给设备的dev_addr[MAX_ADDR_LEN]成员赋值并逐位输出MAC地址 */
dev->dev_addr[i] = SrcMacID[i];
printk("%2.2x%c", dev->dev_addr[i], (i==5) ? ' ' : ':');
}
printk("\n");
SET_MODULE_OWNER(dev); /* 该宏定义于<linux/net_device.h>中。在2.6版的内核中该宏已没有意义 */
dev->priv = kmalloc(sizeof(struct nic_8019_priv), GFP_KERNEL); /*为设备的priv成员分配内存空间以备用 */
if(dev->priv == NULL) /* 若分配失败则返回-ENOMEM */
return -ENOMEM;
memset(dev->priv, 0, sizeof(struct nic_8019_priv)); /* 若分配成功则将该内存区域全初始化为0 */
spin_lock_init(&((struct nic_8019_priv *) dev->priv)->lock); /* 初始化设备的priv成员的自旋锁 */
return 0;
}
/***** 定义设备对象 *****/
static struct net_device nic_8019_netdevs = { /* 该操作定义了一个struct net_device类型的设备对象,并将其init成员 */
init: nic_8019_init, /* 设置为nic_8019_init函数以待调用 */
};
/***** 模块的初始化函数 *****/
int __init nic_8019_init_module(void)
{
int result;
TRACE("init_module\n"); /* 输出跟踪信息 */
printk(KERN_INFO "%s", version); /* 输出版本信息 */
/* 注册设备。该过程会调用设备的init成员所指向的初始化函数(即nic_8019_init)来对设备进行初始化。
注册网络设备,该函数执行成功时会返回1,否则为0。*/
if((result = register_netdev(&nic_8019_netdevs)))
printk("Rtl8019as eth: Error %i registering device \"%s\"\n", result, nic_8019_netdevs.name);
return result ? 0 : -ENODEV; /* 若注册成功则返回0,否则返回-ENODEV */
}
/***** 设备的打开函数 *****/
static int nic_8019_open(struct net_device *dev)
{
int i,j;
MOD_INC_USE_COUNT; /* 该宏定义于<linux/module.h>中,2.4的内核中用它来累计模块的调用次数,
每次打开模块时需要累加该值。 */
TRACE("open\n"); /* 输出跟踪信息 */
disable_irq(dev->irq); /* 在注册中断响应函数之前应先禁用设备的中断功能 */
/* 注册设备的中断响应函数 */
if (request_irq(dev->irq, &nic_8019_rx, SA_INTERRUPT, "eth rx isr", dev)) {
printk(KERN_ERR "Rtl8019: Can't get irq %d\n", dev->irq);
return -EAGAIN; /* 若注册失败则返回-EAGAIN通知内核重新调用open函数 */
}
/* 激活Rtl8019as */
SetRegPage(3); /* 切换到Rtl8019寄存器表的页3 */
outportb(CR9346, 0xcf); /* 将9346CR寄存器的EEM1和EEM0都设置为1,以便启用配置寄存器CONFIG3的写入功能 */
outportb(CONFIG3, 0x60); /* 清除Rtl8019的sleep(休眠)和pwrdn(低功耗)模式, 将led0设置为led_col(响应传输冲突),
将led1设置为led_crs(响应数据传输,包括发送和接收)。*/
outportb(CR9346, 0x3f); /* 禁用配置寄存器CONFIG3的写入功能 */
/* 初始化Rtl8019as */
outportb(RstAddr, 0x5a); /* 重置寄存器(?) */
i = 20000;
while(i--); /* 作少许延时 */
SetRegPage(0); /* 切换到Rtl8019寄存器表的页0 */
inportb(ISR); /* 读取中断状态寄存器以便清空它(?) */
outportb(BaseAddr, 0x21); /* 执行Rtl8019的stop命令,中止任何数据传输 */
outportb(Pstart, RPSTART); /* 设置Rtl8019的RAM中接收缓冲区的起始地址为RPSTART(0x4c) */
outportb(Pstop, RPSTOP); /* 设置Rtl8019的RAM中接收缓冲区的结束地址为RPSTOP(0x80) */
outportb(BNRY, RPSTART); /* 界线寄存器。该寄存器的值通常是接收缓冲区内用户已经读取的最后页地址。
它的作用是防止接收缓冲区的未读数据被覆盖。在这里将它设置为缓冲区的起始地址,
表示还未进行任何读取操作。 */
outportb(TPSR, SPSTART); /* 设置Rtl8019的RAM中发送缓冲区的起始地址为SPSTART(0x40) */
outportb(RCR, 0xcc); /* 设置Rtl8019的接收配置值为0xcc。意义如下:
1.将接收的数据包缓存到内存;
2.进行MAC地址比较;
3.支持多点传送;
4.不接收小于64b的包;
5.不接收发生传输错误的包。*/
outportb(TCR, 0xe0); /* 设置Rtl8019的接收配置值为0xe0。意义如下:
1.启用传输冲突的抵消功能;
2.禁用常规传输的自动发送功能;
3.设置传输方式为常规模式;
4.启用CRC校验功能。*/
#ifdef RTL8019_OP_16 /* 该宏用来标志是否进行的是16位操作 */
outportb(DCR, 0xc9); /* 设置Rtl8019的数据配置为0xc9, 16位的DMA */
#else
outportb(DCR, 0xc8); /* 设置Rtl8019的数据配置为0xc8, 8位的DMA */
#endif
outportb(IMR, 0x03); /* 设置Rtl8019的中断掩码位0x03,即启用接收和发送中断 */
outportb(ISR, 0xff); /* 清空Rtl8019的中断状态寄存器 */
SetRegPage(1); /* 切换到Rtl8019寄存器表的页1 */
for(i=0; i<6; i++) /* 将设备的MAC地址输入到Page1中的PAR0-5寄存器中 */
#ifdef RTL8019_OP_6
outportb(BaseAddr+(1+i)*2, dev->dev_addr[i]);
#else
outportb(BaseAddr+(1+i), dev->dev_addr[i]);
#endif
outportb(CURR, RPSTART+1); /* 写页地址寄存器。该寄存器存储的是用于放置下一个数据包的起始页地址。
这里将它设置为缓冲区的起始页地址加1,表示当前没有收到任何数据。*/
outportb(MAR0, 0x00); /* 设置在Hash校验时用于对多点传送地址进行过滤的位值 */
outportb(MAR1, 0x41);
outportb(MAR2, 0x00);
outportb(MAR3, 0x80);
outportb(MAR4, 0x00);
outportb(MAR5, 0x00);
outportb(MAR6, 0x00);
outportb(MAR7, 0x00);
outportb(BaseAddr, 0x22); /* 切换到Rtl8019寄存器表的页0并执行Rtl8019的start命令,准备进行数据传输 */
rBNRY = RPSTART; /* 将Rtl8019接收缓冲区中的当前读取地址(即起始地址)保存备用 */
enable_irq(dev->irq); /* 启用设备的中断功能 */
/* 开启设备的数据传输队列(即从此允许设备进行收发数据包) */
netif_start_queue(dev);
return 0;
}
/***** 设备的发送函数 *****/
static int nic_8019_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
int i;
#ifdef RTL8019_OP_16
u16 len,TxLen;
u16 *data;
#else
int len, TxLen; /* 向Rtl8019的RAM写入的数据长度及Rtl8019向外发送的数据长度 */
u8 *data; /* 数据缓冲区 */
#endif
struct nic_8019_priv *priv = (struct nic_8019_priv *) dev->priv; /* 获得设备的私有结构体 */
TRACE("start_xmit\n"); /* 输出跟踪信息 */
len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len; /* 获取数据包的长度,如果该长度小于最小长度,就等于最小长度 */
TRACE("\nTx Length = %i,%x,%x\n", len, skb->data[12], skb->data[13]); /* 输出设备报头的最后两位(即类型字段) */
#ifdef RTL8019_OP_16
data =(u16*) skb->data;
#else
data = (u8*) skb->data; /* 获取数据包中的数据并进行类型转换 */
#endif
/***** 主机CPU先以远程DMA方式将数据写入到Rtl8019的RAM中 *****/
outportb(BaseAddr,0x22); /* 切换到Rtl8019寄存器表的页0并中止远程DMA传输 */
if (inportb(BaseAddr)&4) /* 如果最后一次远程DMA操作没有完成,则返回1表示设备繁忙的错误,然后重新执行该函数 */
return 1;
#ifdef bug_fix_for_write
//read page 42,0,42,0 before write if you have problem
#endif
outportb(RSAR0, 0); /* 设置远程DMA传输的起始地址为SPSTART(0x40)。先打入低8位,*/
outportb(RSAR1, SPSTART); /* 后打入高8位 */
outportb(RBCR0, len&0xff); /* 设置远程DMA传输的数据长度。先打入低8位,*/
outportb(RBCR1, len>>8); /* 后打入高8位 */
outportb(BaseAddr, 0x12); /* 开始远程DMA写入操作 */
dev->trans_start = jiffies; /* 记录此次发送操作的开始时间 */
#ifdef RTL8019_OP_16
TxLen=(len+1)/2; /* 如果是16位DMA操作则将长度减半 */
#else
TxLen=len; /* 否则直接获得数据长度 */
#endif
for(i=0; i<TxLen; i++) {
#ifdef RTL8019_OP_16
outportw(RWPORT, data[i]);
TRACE("%2X,%2X,",data[i]&0xff,data[i]>>8);
#else
outportb(RWPORT, data[i]); /* 通过Rtl8019的Remote DMA Port将数据包内的数据拷贝到Rtl8019的RAM中 */
TRACE("%2X,",skb->data[i]); /* 同时将数据作为跟踪信息输出以便查阅 */
#endif
}
TRACE("\n");
while(inportb(BaseAddr)&4); /* 等待传输操作完成 */
/***** 然后Rtl8019再将自己RAM中的数据发送出去 *****/
outportb(TPSR, SPSTART); /* 设置Rtl8019的RAM中要发送数据包的起始地址为SPSTART(0x40) */
outportb(TBCR0, len&0xff); /* 设置要传输的数据包长度。先打入低8位, */
outportb(TBCR1, len>>8); /* 后打入高8位, */
outportb(BaseAddr, 0x1e); /* 开始发送数据包 */
dev_kfree_skb(skb); /* 释放Socket缓冲区 */
return 0;
}
/***** 设备的中断响应函数 *****/
irqreturn_t nic_8019_rx(int irq, void *dev_id, struct pt_regs *regs)
{
u8 RxPageBeg, RxPageEnd; /* 接收缓冲区内数据的起始地址和结束地址 */
u8 RxNextPage; /* 下一个数据页的起始地址 */
u8 RxStatus; /* 中断状态 */
#ifdef RTL8019_OP_16
u16 *data,temp;
u16 i, RxLength,RxLen;
#else
u8* data; /* 数据缓冲区 */
int i, RxLength, RxLen; /* 数据总长度及已获得的长度 */
#endif
struct sk_buff *skb; /* 定义Socket缓冲区 */
struct net_device *dev = (struct net_device *) dev_id; /* 获得设备对象 */
struct nic_8019_priv *priv = (struct nic_8019_priv *) dev->priv; /* 获得设备的私有结构体 */
TRACE("TX/RX Interupt!\n"); /* 输出跟踪信息 */
spin_lock(&priv->lock); /* 获得自旋锁 */
SetRegPage(0); /* 切换到Rtl8019寄存器表的页0 */
outportb(BNRY, rBNRY); /* 将边界寄存器的值设置为接收缓冲区的起始地址(rBNRY在open函数中以赋值) */
RxStatus = inportb(ISR); /* 获得引发此次中断的操作类型 */
/***** 若操作类型为发送操作 *****/
if (RxStatus & 2) {
outportb(ISR, 0x2); /* 清空中断状态寄存器中的发送状态位 */
priv->stats.tx_packets++; /* 累加设备私有结构体中的已接收数据包的个数*/
TRACE("transmit one packet complete!\n"); /* 输出跟踪信息 */
}
/***** 若操作类型为接收操作 *****/
if (RxStatus & 1) {
TRACE("Receivex packet....\n"); /* 输出跟踪信息 */
outportb(ISR, 0x1); /* 清空中断状态寄存器中的接收状态位 */
SetRegPage(1); /* 切换到Rtl8019寄存器表的页1 */
RxPageEnd = inportb(CURR); /* 将页结束地址初始化为下一个数据页的起始地址 */
SetRegPage(0); /* 切换到Rtl8019寄存器表的页0 */
RxPageBeg = rBNRY+1; /* 将页起始地址初始化为边界地址加1 */
if(RxPageBeg>=RPSTOP) /* 如果起始地址大于或等于接收缓冲区的结束地址,*/
RxPageBeg = RPSTART; /* 那么就将它设置为接收缓冲区的起始地址 */
outportb(BaseAddr, 0x22); /* 中止远程DMA操作 */
/***** 主机CPU从Rtl8019的RAM中将数据包的前4位读出,这4位标志是Rtl8019单独添加的,
通过其中的长度位来判断本次封包的长度是否合法 *****/
//outport(RSAR0, RxPageBeg<<8);
//outport(RBCR0, 256);
outportb(RSAR0, 0); /* 设置远程DMA传输的读取地址 */
outportb(RSAR1, RxPageBeg);
outportb(RBCR0, 4); /* 设置远程DMA传输的数据长度 */
outportb(RBCR1, 0);
outportb(BaseAddr, 0xa); /* 执行Rtl8019的start命令起动远程DMA读取操作 */
#ifdef RTL8019_OP_16
temp = inportw(RWPORT);
RxNextPage = temp>>8;
RxStatus = temp&0xff;
RxLength = inportw(RWPORT);
#else /* 获得前4个字节 */
RxStatus = inportb(RWPORT); /* 获得接收状态 */
RxNextPage = inportb(RWPORT); /* 获得下一数据页的起始地址 */
RxLength = inportb(RWPORT); /* 获得整个数据包长度 */
RxLength |= inportb(RWPORT)<<8;
#endif
TRACE("\nRxBeg = %x, RxEnd = %x, nextpage = %x, size = %i\n", RxPageBeg, RxPageEnd, RxNextPage, RxLength);
if(RxLength == 0){ /* 如果数据长度为0,则输出调试信息并返回IRQ_HANDLED表示已有中断被触发 */
TRACE("Rxlength == 0\n");
return IRQ_HANDLED;
}
RxLength -= 4; /* 获得实际有效数据的长度 */
if (RxLength>ETH_FRAME_LEN) { /* 如果数据长度大于封包最大长度 */
if (RxPageEnd==RPSTART) /* 如果接收数据的结束页地址等于缓冲区的起始地址,那么说明所有数据已被读取, */
rBNRY = RPSTOP-1; /* 则将边界地址设置为缓冲区的结束页地址减1,表示已经读取到缓冲区尾部。*/
else
rBNRY = RxPageEnd-1; /* 否则将边界地址设置为接收数据的结束页地址减1 */
outportb(BNRY, rBNRY); /* 设置边界寄存器的值 */
TRACE("RxLength more long than %x\n", ETH_FRAME_LEN); /* 输出数据过长的跟踪信息 */
return IRQ_HANDLED; /* 返回IRQ_HANDLED表示已有中断被触发 */
}
#ifdef RTL8019_OP_16
skb = dev_alloc_skb(RxLength+2);
#else
skb = dev_alloc_skb(RxLength); /* 为Socket缓冲区分配空间 */
#endif
if (!skb) { /* 如果分配失败 */
TRACE("Rtl8019as eth: low on mem - packet dropped\n"); /* 输出失败的跟踪信息 */
priv->stats.rx_dropped++; /* 累加设备私有结构体中的丢掉的数据包计数 */
return IRQ_HANDLED; /* 返回IRQ_HANDLED表示已有中断被触发 */
}
skb->dev = dev; /* 设置Socket缓冲区中的设备句柄 */
skb_reserve(skb, 2); /* 在Socket缓冲区的前面预留2个字节的空间 */
skb_put(skb, RxLength); /* 在Socket缓冲区内为接收数据分配空间并更新缓冲区的tail和len成员 */
#ifdef RTL8019_OP_16
data = ( u16 *)skb->data;
#else
data = ( u8 *)skb->data; /* 获取Socket缓冲区内的数据起始地址 */
#endif
// eth_copy_and_sum(skb, data, len, 0);
outportb(RSAR0, 4); /* 设置远程DMA传输的读取地址 */
outportb(RSAR1, RxPageBeg);
outportb(RBCR0, RxLength); /* 设置远程DMA传输的数据长度 */
outportb(RBCR1, RxLength>>8);
outportb(BaseAddr, 0xa); /* 执行Rtl8019的start命令起动远程读取操作 */
#ifdef RTL8019_OP_16
i = 2;
data -= 2;
RxLen=(RxLength+1)/2;
#else
i = 4;
data -= 4; /* 将数据缓冲区的地址提前4个字节,以便获取实际数据 */
RxLen=RxLength; /* 获取接收的数据长度 */
#endif
for(; RxLen--;) {
#ifdef RTL8019_OP_16
static const int cmp_val = 0x7f;
#else
static const int cmp_val = 0xff;
#endif
if (!(i & cmp_val)) {
outportb(BNRY, RxPageBeg);
RxPageBeg++;
if(RxPageBeg>=RPSTOP)
RxPageBeg = RPSTART;
}
#ifdef RTL8019_OP_16
data[i++] = inportw(RWPORT);
TRACE("%2X,%2X,", data[i-1]&0xff,data[i-1]>>8);
#else
data[i++] = inportb(RWPORT); /* 从Rtl8019的Remote DMA port读取数据 */
TRACE("%2X,", data[i-1]); /* 将数据作为跟踪信息输出 */
#endif
}
TRACE("\n");
outportb(BNRY, RxPageBeg); /* 设置边界寄存器的值 */
rBNRY = RxPageBeg; /* 保存当前读页地址 */
skb->protocol = eth_type_trans(skb, dev);/* 设置skb->pkt_type用来判断该包是否属于该主机,并返回一个协议编号 */
TRACE("\nprotocol=%x\n", skb->protocol); /* 输出协议编号 */
priv->stats.rx_packets++; /* 累计接收到的数据包计数 */
priv->stats.rx_bytes +=RxLength; /* 累计接收到的字节计数 */
netif_rx(skb); /* 如果skb->pkt_type为PACKET_HOST,则通知内核已经收到新的数据包,
如果为PACKET_OTHERHOST,则丢掉该包。*/
} else {
outportb(ISR, 0xfe); /* 清空Rtl8019的中断状态寄存器(接收状态位除外) */
}
spin_unlock(&priv->lock); /* 释放自旋锁 */
return IRQ_HANDLED;
}
/***** 获得设备的统计信息 *****/
static struct net_device_stats *nic_8019_get_stats(struct net_device *dev)
{
struct nic_8019_priv *priv = (struct nic_8019_priv *) dev->priv; /* 获得设备私有结构体的指针 */
TRACE("get_stats\n"); /* 输出跟踪信息 */
return &priv->stats; /* 返回设备的统计信息 */
}
/***** 设备的终止函数 *****/
static int nic_8019_stop(struct net_device *dev)
{
TRACE("stop\n"); /* 输出跟踪信息 */
SetRegPage(3); /* 切换到Rtl8019寄存器表的页3 */
outportb(CR9346, 0xcf); /* 将9346CR寄存器的EEM1和EEM0都设置为1,以便启用配置寄存器CONFIG3的写入功能 */
outportb(CONFIG3, 0x66); /* 进入Rtl8019的sleep(休眠)和pwrdn(低功耗)模式, 将led0设置为led_col(响应传输冲突),
将led1设置为led_crs(响应数据传输,包括发送和接收)。*/
outportb(CR9346, 0x3f); /* 禁用配置寄存器CONFIG3的写入功能 */
free_irq(dev->irq, dev); /* 释放设备中断 */
netif_stop_queue(dev); /* 停止设备的传输队列 */
MOD_DEC_USE_COUNT; /* 递减该模块的调用次数 */
return 0;
}
/***** 模块的清理函数 *****/
void __exit nic_8019_cleanup(void)
{
TRACE("cleanup\n"); /* 输出跟踪信息 */
kfree(nic_8019_netdevs.priv); /* 释放设备的私有结构体 */
unregister_netdev(&nic_8019_netdevs); /* 注销设备 */
return;
}
/* 指定模块的初始化和清理函数 */
module_init(nic_8019_init_module);
module_exit(nic_8019_cleanup);
/* 设置模块信息 */
MODULE_DESCRIPTION("Rtl8019as ethernet driver");
MODULE_AUTHOR("antiscle <[email protected]>");
MODULE_LICENSE("GPL");
/*下列宏定义于<linux/if_ether.h>中
ETH_ALEN 网络设备的硬件地址(即MAC地址)长度,单位为一个八位字节,该值通常为6。
ETH_DATA_LEN 每次传输的最大数据量(不包括14位设备报头)。该值通常为1500。
ETH_HLEN 设备报头长度。该值通常为14。(包括目的地址(DA长度6)、源地址(SA长度6)、类型字段(TYPE长度2))
ETH_ZLEN 整个数据包的最小长度。该值通常为60。
*/
/*MAX_ADDR_LEN 该宏定义于<linux/netdevice.h>中,指的是网络设备地址(即MAC地址)的最大长度,该值通常为32。*/
/////////////// II.与网络设备相关的结构体: //////////////////////////////////
/********************************* struct net_device *************************************************/
/*为了屏蔽网络环境中物理网络设备的多样性,Linux对所有的物理网络设备进行抽象并定义了一个统一的概念,
称之为接口(Interface)。每个接口在内部都表现为一个这样的结构体。以下为该结构体的定义:*/
struct net_device
{
/* 以下为该结构体的可见成员,用户可从Space.c等文件中查看到它们*/
char name[IFNAMSIZ]; /* 网络设备的名称*/
unsigned long mem_end; /* 设备发送和接收数据时需要占用内存空间,该值为内存区域的结束地址*/
unsigned long mem_start; /* 共享内存区域的起始地址*/
unsigned long base_addr; /* 设备I/O操作的基地址,由驱动程序设置,该成员可通过ifconfig来查看或更新*/
unsigned int irq; /* 设备的中断号,该成员可通过ifconfig来查看或更新*/
/*
* 下列成员只用于部分设备,它们不会出现在Space.c文件中
*/
unsigned char if_port; /* 端口传输特性,用于多端口设备,可选值如下:
enum {
IF_PORT_UNKNOWN = 0, //未知方式
IF_PORT_10BASE2, //10兆同轴方式
IF_PORT_10BASET, //10兆双绞方式
IF_PORT_AUI,
IF_PORT_100BASET,
IF_PORT_100BASETX,
IF_PORT_100BASEFX
};*/
unsigned char dma; /* 设备的DMA通道,通常只在与外部总线通讯时才有意义,通过ifconfig可查看该成员*/
unsigned long state; /* 设备的当前状态*/
struct net_device *next; /* 指向全局设备链表中下一个设备的指针*/
int (*init)(struct net_device *dev); /* 设备的初始化函数,仅需调用一次 */
/* 至此,在Space.c文件中可见的成员介绍完毕 */
//////////////////////////////////////////////////////////////////////////////////////////
struct net_device *next_sched;
/* 设备索引,唯一的设备标识 */
int ifindex;
int iflink;
struct net_device_stats* (*get_stats)(struct net_device *dev);
struct iw_statistics* (*get_wireless_stats)(struct net_device *dev);
/*用于处理无线传输的函数,ioctl的替代品,详情查看 <net/iw_handler.h>*/
struct iw_handler_def * wireless_handlers;
struct ethtool_ops *ethtool_ops;
/* 至此,该结构体的可见成员已介绍完毕,以下的所有成员都属于系统底层,
* 而且可能会有不定期变动。*/
/* 在未来处理网络设备掉电的代码中也许会用到以下成员:*/
unsigned long trans_start; /* 进行最后一次发送操作的时间(以jiffies表示)*/
unsigned long last_rx; /* 进行最后一次接收操作的时间*/
unsigned short flags; /* 设备标记。可用标记查看<linux/if.h>*/
unsigned short gflags; /* 全局标记(?)*/
unsigned short priv_flags; /* 类似于flags,但用户空间不可见 */
unsigned short unused_alignment_fixer; /* Because we need priv_flags,
* and we want to be 32-bit aligned.
*/
unsigned mtu; /* 每次传输的最大数据量(即ETH_DATA_LEN),缺省值为1500字节,该成员可通过ifconfig更改*/
unsigned short type; /* 设备类型。(ARP)地址解析协议根据该成员来判断接口支持何种硬件地址。对于以太网设备
来说,合适的值为ARPHRD_ETHER。在<linux/if_arp.h>中有所有设备类型。该成员通过ether_setup设置*/
unsigned short hard_header_len;/* 设备报头长度。即位于发送数据包中IP报头之前的数据长度(即ETH_HLEN),
以太网设备的该值为14。*/
void *priv; /* 相当于filp->private_data。通过alloc_netdev设置,通过netdev_priv访问*/
struct net_device *master; /* 该指针指向当前设备组中该设备所属的主设备*/
/* 设备地址相关信息 */
unsigned char broadcast[MAX_ADDR_LEN]; /* 广播地址。由ether_setup 进行分配*/
unsigned char dev_addr[MAX_ADDR_LEN]; /* 设备地址(MAC地址),通过读取设备信息获得*/
unsigned char addr_len; /* 设备地址长度(MAC地址),通常为6*/
struct dev_mc_list *mc_list; /* 用于多点传送的MAC地址列表 */
int mc_count; /* 多点传送时MAC地址列表中的元素个数 */
int promiscuity;
int allmulti;
int watchdog_timeo; /* 该成员是一个以jiffies为单位的时间数。表示的是网络层判定传输超时的最小时间标准*/
struct timer_list watchdog_timer; /* 用来为超时计数的定时器(?)*/
/* Protocol specific pointers */
void *atalk_ptr; /* AppleTalk link */
void *ip_ptr; /* IPv4 specific data */
void *dn_ptr; /* DECnet specific data */
void *ip6_ptr; /* IPv6 specific data */
void *ec_ptr; /* Econet specific data */
void *ax25_ptr; /* AX.25 specific data */
struct list_head poll_list; /* Link to poll list */
int quota;
int weight;
struct Qdisc *qdisc;
struct Qdisc *qdisc_sleeping;
struct Qdisc *qdisc_ingress;
struct list_head qdisc_list;
unsigned long tx_queue_len; /* Max frames per queue allowed */
/* ingress path synchronizer */
spinlock_t ingress_lock;
/* hard_start_xmit synchronizer */
spinlock_t xmit_lock; /* 该锁用来防止同一时间内对hard_start_xmit函数进行多次调用*/
/* cpu id of processor entered to hard_start_xmit or -1,
if nobody entered there.
*/
int xmit_lock_owner; /* 该成员指的是获得了xmit_lock的CPU的ID号。-1表示没有当前CPU获得xmit_lock*/
/* device queue lock */
spinlock_t queue_lock;
/* Number of references to this device */
atomic_t refcnt;
/* delayed register/unregister */
struct list_head todo_list;
/* device name hash chain */
struct hlist_node name_hlist;
/* device index hash chain */
struct hlist_node index_hlist;
/* register/unregister state machine */
enum { NETREG_UNINITIALIZED=0,
NETREG_REGISTERING, /* called register_netdevice */
NETREG_REGISTERED, /* completed register todo */
NETREG_UNREGISTERING, /* called unregister_netdevice */
NETREG_UNREGISTERED, /* completed unregister todo */
NETREG_RELEASED, /* called free_netdev */
} reg_state;
/* 网络设备的特性 */
int features;
#define NETIF_F_SG 1 /* 分散/整合的I/O操作。标志设备可以将分散的数据包整合后再发送 */
#define NETIF_F_IP_CSUM 2 /* 只对TCP/UDP包进行校验 */
#define NETIF_F_NO_CSUM 4 /* 设备无需进行包校验操作 */
#define NETIF_F_HW_CSUM 8 /* 校验所有类型的数据包 */
#define NETIF_F_HIGHDMA 32 /* 可直接存取内存中的高地址 */
#define NETIF_F_FRAGLIST 64 /* 分散/整合的I/O操作。标志设备可以在接收到分散的数据后将其整合*/
#define NETIF_F_HW_VLAN_TX 128 /* Transmit VLAN hw acceleration */
#define NETIF_F_HW_VLAN_RX 256 /* Receive VLAN hw acceleration */
#define NETIF_F_HW_VLAN_FILTER 512 /* Receive filtering on VLAN */
#define NETIF_F_VLAN_CHALLENGED 1024 /* Device cannot handle VLAN packets */
#define NETIF_F_TSO 2048 /* Can offload TCP/IP segmentation */
#define NETIF_F_LLTX 4096 /* LockLess TX */
/* 当设备与网络断开时调用该函数 */
void (*uninit)(struct net_device *dev);
/* 设备的析构函数,当设备的所有外部引用都消失后调用该函数 */
void (*destructor)(struct net_device *dev);
/********** 设备操作函数指针 ***********/
int (*open)(struct net_device *dev); /*设备的打开函数。当ifconfig激活设备后设备就会被打开,
该函数的工作是注册设备所需的系统资源(I/O端口,中断号及DMA通道等),
打开硬件设备并进行设备所需的其他设置。 */
int (*stop)(struct net_device *dev); /*设备的终止函数。当接口被析构时设备就会被停止。该函数执行与open函数相反的操作。*/
int (*hard_start_xmit) (struct sk_buff *skb, /*发送一个完整的数据包*/
struct net_device *dev);
#define HAVE_NETDEV_POLL
int (*poll) (struct net_device *dev, int *quota); /*由NAPI驱动程序提供的以轮询方式工作的设备操作函数*/
int (*hard_header) (struct sk_buff *skb, /*该函数根据源设备及目标设备的地址组合一个设备报头。*/
struct net_device *dev, /*以太网设备的该函数默认为eth_header。*/
unsigned short type, /*该函数在调用hard_start_xmit之前被调用。*/
void *daddr,
void *saddr,
unsigned len);
int (*rebuild_header)(struct sk_buff *skb); /*该函数负责重建设备报头以完善其信息。
它在地址解析完成后,发送首个数据包之前被调用。*/
#define HAVE_MULTICAST
void (*set_multicast_list)(struct net_device *dev); /*当设备多点传送的地址列表和设备标志被更改时调用该函数。*/
#define HAVE_SET_MAC_ADDR
int (*set_mac_address)(struct net_device *dev, /*在设备支持的情况下设置设备的MAC地址。*/
void *addr);
#define HAVE_PRIVATE_IOCTL
int (*do_ioctl)(struct net_device *dev, /*实现设备特有的ioctl命令。不需要时可将参数1设为NULL。*/
struct ifreq *ifr, int cmd);
#define HAVE_SET_CONFIG
int (*set_config)(struct net_device *dev, /*旧有的更改设备设置的函数。目前的设备不需要该函数。*/
struct ifmap *map);
#define HAVE_HEADER_CACHE
int (*hard_header_cache)(struct neighbour *neigh, /*将地址解析的结果数据填充到hh_cache结构体中。*/
struct hh_cache *hh); /*以太网设备的该函数默认为eth_header_cache 。*/
void (*header_cache_update)(struct hh_cache *hh, /*当地址解析的结果变更时更新hh_cache结构体中的信息。*/
struct net_device *dev, /*以太网设备的默认函数是eth_header_cache_update。*/
unsigned char * haddr);
#define HAVE_CHANGE_MTU
int (*change_mtu)(struct net_device *dev, int new_mtu); /*改变设备单次传输的最大数据量*/
#define HAVE_TX_TIMEOUT
void (*tx_timeout) (struct net_device *dev); /*当一个数据包发送失败时(可能是错过一次中断或设备被锁),
该函数负责处理相应问题并恢复传输。*/
void (*vlan_rx_register)(struct net_device *dev,
struct vlan_group *grp);
void (*vlan_rx_add_vid)(struct net_device *dev,
unsigned short vid);
void (*vlan_rx_kill_vid)(struct net_device *dev,
unsigned short vid);
int (*hard_header_parse)(struct sk_buff *skb, /*该函数从skb包含的数据包中取出源地址,将其复制到haddr指向*/
unsigned char *haddr); /*的缓冲区中,并返回源地址长度。以太网设备的默认函数为eth_header_parse*/
int (*neigh_setup)(struct net_device *dev, struct neigh_parms *);
int (*accept_fastpath)(struct net_device *, struct dst_entry*);
#ifdef CONFIG_NETPOLL
int netpoll_rx;
#endif
#ifdef CONFIG_NET_POLL_CONTROLLER
void (*poll_controller)(struct net_device *dev); /*当禁用中断时,该函数用于请求驱动程序检查设备事件状况。*/
#endif
/* bridge stuff */
struct net_bridge_port *br_port;
#ifdef CONFIG_NET_DIVERT
/* this will get initialized at each interface type init routine?*/
struct divert_blk *divert;
#endif /* CONFIG_NET_DIVERT */
/* class/net/name entry */
struct class_device class_dev;
/* how much padding had been added by alloc_netdev() */
int padded;
};
/********************************** struct net_device_stats ***********************************/
/*该结构体用来描述网络设备进行数据传输的统计信息。以下为该结构体的定义:*/
struct net_device_stats
{
unsigned long rx_packets; /* 已接收到的数据包总数 */
unsigned long tx_packets; /* 已发送的数据包总数 */
unsigned long rx_bytes; /* 已接收到的字节总数 */
unsigned long tx_bytes; /* 已发送的字节总数 */
unsigned long rx_errors; /* 接收失败的数据包总数 */
unsigned long tx_errors; /* 传输失败的数据包总数 */
unsigned long rx_dropped; /* 由于接收缓冲区的空间不足而被丢掉的数据包总数 */
unsigned long tx_dropped; /* 由于发送缓冲区的空间不足而被丢掉的数据包总数 */
unsigned long multicast; /* 多点传送时接收到的数据包总数 */
unsigned long collisions; /* 由于媒介堵塞所引发的冲突次数 */
/* 接收失败的各种情况 */
unsigned long rx_length_errors; /* 接收的数据长度不符 */
unsigned long rx_over_errors; /* 接收缓冲区溢出 */
unsigned long rx_crc_errors; /* CRC校验错误 */
unsigned long rx_frame_errors; /* 帧同步错误 */
unsigned long rx_fifo_errors; /* 先入先出操作超度 */
unsigned long rx_missed_errors; /* 丢包 */
/* 发送失败的各种情况 */
unsigned long tx_aborted_errors;
unsigned long tx_carrier_errors;
unsigned long tx_fifo_errors;
unsigned long tx_heartbeat_errors;
unsigned long tx_window_errors;
/* for cslip etc */
unsigned long rx_compressed;
unsigned long tx_compressed;
};
/********************************** struct sk_buff ***********************************/
struct sk_buff {
/* 这两个成员必须放在最前面 */
struct sk_buff *next;
struct sk_buff *prev;
struct sk_buff_head *list;
struct sock *sk;
struct timeval stamp;
struct net_device *dev; /* 发送或接收该缓冲区的网络设备 */
struct net_device *input_dev;
struct net_device *real_dev;
union {
struct tcphdr *th;
struct udphdr *uh;
struct icmphdr *icmph;
struct igmphdr *igmph;
struct iphdr *ipiph;
struct ipv6hdr *ipv6h;
unsigned char *raw;
} h; /* 指向数据包报头中的传输层报头*/
union {
struct iphdr *iph;
struct ipv6hdr *ipv6h;
struct arphdr *arph;
unsigned char *raw;
} nh; /* 指向数据包报头中的网络层报头*/
union {
unsigned char *raw;
} mac; /* 指向数据包报头中的链路层报头*/
struct dst_entry *dst;
struct sec_path *sp;
/*
* This is the control buffer. It is free to use for every
* 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.
*/
char cb[40];
unsigned int len, /* 数据包中数据总长度 */
data_len, /* 数据包中分段存储的数据长度。除非使用了分散/整合的I/O操作,否则该成员始终为0*/
mac_len, /* MAC地址长度(?) */
csum;
unsigned char local_df,
cloned,
pkt_type, /* 数据包类别。该成员由eth_type_trans 负责设置。常用值包括:
PACKET_HOST (发送给本机的包)
PACKET_OTHERHOST (发送给其他主机的包)
PACKET_BROADCAST (广播发送的包)
PACKET_MULTICAST (多点传送的包)*/
ip_summed; /* 数据包的校验策略 */
__u32 priority;
unsigned short protocol,
security;
void (*destructor)(struct sk_buff *skb);
#ifdef CONFIG_NETFILTER
unsigned long nfmark;
__u32 nfcache;
__u32 nfctinfo;
struct nf_conntrack *nfct;
#ifdef CONFIG_NETFILTER_DEBUG
unsigned int nf_debug;
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
struct nf_bridge_info *nf_bridge;
#endif
#endif /* CONFIG_NETFILTER */
#if defined(CONFIG_HIPPI)
union {
__u32 ifield;
} private;
#endif
#ifdef CONFIG_NET_SCHED
__u32 tc_index; /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
__u32 tc_verd; /* traffic control verdict */
__u32 tc_classid; /* traffic control classid */
#endif
#endif
/* 这些成员必须放在最后,详细原因查看alloc_skb() */
unsigned int truesize;
atomic_t users;
/* 以下成员用于对数据包中的数据进行寻址*/
unsigned char *head, /* 指向所分配空间的起始地址 */
*data, /* 指向有效数据的起始地址 */
*tail, /* 指向有效数据的结束地址 */
*end; /* 指向所分配空间的结束地址 */
};
////////////////////// III.rtl8019.c源码分析 /////////////////////////////////
/******************** 变量、宏、结构体 *******************/
#define outportb(port, data) *((volatile u8 *)(port)) = (u8)(data) /* 8位的端口写入函数 */
#define inportb(port) *((volatile u8 *)(port)) /* 8位的端口读取函数 */
#define outportw(port, data) *((volatile u16 *)(port)) = (u16)(data) /* 16位的端口写入函数 */
#define inportw(port) *((volatile u16 *)(port)) /* 16位的端口读取函数 */
#define ETH_FRAME_LEN 1514 /* 整个封包的最大长度 */
/* Rtl8019的缓冲区范围为0x40~0x80,大小为16k,分为64页,每页大小256b。其中: */
#define RPSTART 0x4c /* Rtl8019的RAM中接收缓冲区的起始页地址 */
#define RPSTOP 0x80 /* Rtl8019的RAM中接收缓冲区的结束页地址 */
#define SPSTART 0x40 /* Rtl8019的RAM中发送缓冲区的起始页地址 */
static int timeout = 100; /* 发送超时 tx watchdog ticks 100 = 1s */
static char *version = "Samsung S3C44B0 Rtl8019as driver version 1.0\n"; /* 版本号 */
static u8 rBNRY; /* 当前读取的页地址 */
static u8 SrcMacID[ETH_ALEN] = {0x00,0x80,0x48,0x12,0x34,0x56}; /* MAC地址 */
/* 设备私有的结构体。用于接收及发送数据包。 */
struct nic_8019_priv {
struct net_device_stats stats; /* 设备的传输统计对象 */
spinlock_t lock; /* 自旋锁 */
struct sk_buff *skb; /* socket缓冲区 */
};
/************************ 操作函数 *******************************/
/***** 选择寄存器页(Rtl8019as共有Page0~3四个寄存器页)*****/
static void SetRegPage( u8 PageIdx)
{
u8 temp;
temp = inportb(BaseAddr); /* 读取Rtl8019第0寄存器页中的CR寄存器值 */
temp = (temp&0x3b)|(PageIdx<<6); /* 将寄存器当前值与上3b得出前6位的无效值。CR寄存器的高两位用于设置页号,
所以需要将页号左移6位再进行或运算 */
outportb(BaseAddr, temp); /* 设置CR寄存器的值 */
}
/***** 设备的初始化函数 *****/
static int nic_8019_init(struct net_device *dev)
{
int i;
TRACE("init\n"); /* 输出跟踪信息 */
ether_setup(dev); /* ether_setup用来设置设备的各个成员 */
// 设置设备的操作函数
dev->open = nic_8019_open; /* 设备的打开函数 */
dev->stop = nic_8019_stop; /* 设备的终止函数 */
dev->get_stats = nic_8019_get_stats; /* 设备的统计函数 */
dev->hard_start_xmit = nic_8019_start_xmit; /* 设备的发送函数 */
// 设置设备的操作参数
dev->watchdog_timeo = timeout; /* 设置超时为timeout(100 ticks=1s) */
dev->irq = S3C44B0X_INTERRUPT_EINT1; /* 设置中断号。S3C44B0X_INTERRUPT_EINT1定义于<include/asm/arch/irqsh.h>,默认值为24 */
dev->dma = 0; /* 设置dma通道 */
// 设置MAC地址
printk(KERN_INFO "%s: ", dev->name); /* 输出设备名称以便明确操作 */
for(i=0; i<6; i++) { /* 给设备的dev_addr[MAX_ADDR_LEN]成员赋值并逐位输出MAC地址 */
dev->dev_addr[i] = SrcMacID[i];
printk("%2.2x%c", dev->dev_addr[i], (i==5) ? ' ' : ':');
}
printk("\n");
SET_MODULE_OWNER(dev); /* 该宏定义于<linux/net_device.h>中。在2.6版的内核中该宏已没有意义 */
dev->priv = kmalloc(sizeof(struct nic_8019_priv), GFP_KERNEL); /*为设备的priv成员分配内存空间以备用 */
if(dev->priv == NULL) /* 若分配失败则返回-ENOMEM */
return -ENOMEM;
memset(dev->priv, 0, sizeof(struct nic_8019_priv)); /* 若分配成功则将该内存区域全初始化为0 */
spin_lock_init(&((struct nic_8019_priv *) dev->priv)->lock); /* 初始化设备的priv成员的自旋锁 */
return 0;
}
/***** 定义设备对象 *****/
static struct net_device nic_8019_netdevs = { /* 该操作定义了一个struct net_device类型的设备对象,并将其init成员 */
init: nic_8019_init, /* 设置为nic_8019_init函数以待调用 */
};
/***** 模块的初始化函数 *****/
int __init nic_8019_init_module(void)
{
int result;
TRACE("init_module\n"); /* 输出跟踪信息 */
printk(KERN_INFO "%s", version); /* 输出版本信息 */
/* 注册设备。该过程会调用设备的init成员所指向的初始化函数(即nic_8019_init)来对设备进行初始化。
注册网络设备,该函数执行成功时会返回1,否则为0。*/
if((result = register_netdev(&nic_8019_netdevs)))
printk("Rtl8019as eth: Error %i registering device \"%s\"\n", result, nic_8019_netdevs.name);
return result ? 0 : -ENODEV; /* 若注册成功则返回0,否则返回-ENODEV */
}
/***** 设备的打开函数 *****/
static int nic_8019_open(struct net_device *dev)
{
int i,j;
MOD_INC_USE_COUNT; /* 该宏定义于<linux/module.h>中,2.4的内核中用它来累计模块的调用次数,
每次打开模块时需要累加该值。 */
TRACE("open\n"); /* 输出跟踪信息 */
disable_irq(dev->irq); /* 在注册中断响应函数之前应先禁用设备的中断功能 */
/* 注册设备的中断响应函数 */
if (request_irq(dev->irq, &nic_8019_rx, SA_INTERRUPT, "eth rx isr", dev)) {
printk(KERN_ERR "Rtl8019: Can't get irq %d\n", dev->irq);
return -EAGAIN; /* 若注册失败则返回-EAGAIN通知内核重新调用open函数 */
}
/* 激活Rtl8019as */
SetRegPage(3); /* 切换到Rtl8019寄存器表的页3 */
outportb(CR9346, 0xcf); /* 将9346CR寄存器的EEM1和EEM0都设置为1,以便启用配置寄存器CONFIG3的写入功能 */
outportb(CONFIG3, 0x60); /* 清除Rtl8019的sleep(休眠)和pwrdn(低功耗)模式, 将led0设置为led_col(响应传输冲突),
将led1设置为led_crs(响应数据传输,包括发送和接收)。*/
outportb(CR9346, 0x3f); /* 禁用配置寄存器CONFIG3的写入功能 */
/* 初始化Rtl8019as */
outportb(RstAddr, 0x5a); /* 重置寄存器(?) */
i = 20000;
while(i--); /* 作少许延时 */
SetRegPage(0); /* 切换到Rtl8019寄存器表的页0 */
inportb(ISR); /* 读取中断状态寄存器以便清空它(?) */
outportb(BaseAddr, 0x21); /* 执行Rtl8019的stop命令,中止任何数据传输 */
outportb(Pstart, RPSTART); /* 设置Rtl8019的RAM中接收缓冲区的起始地址为RPSTART(0x4c) */
outportb(Pstop, RPSTOP); /* 设置Rtl8019的RAM中接收缓冲区的结束地址为RPSTOP(0x80) */
outportb(BNRY, RPSTART); /* 界线寄存器。该寄存器的值通常是接收缓冲区内用户已经读取的最后页地址。
它的作用是防止接收缓冲区的未读数据被覆盖。在这里将它设置为缓冲区的起始地址,
表示还未进行任何读取操作。 */
outportb(TPSR, SPSTART); /* 设置Rtl8019的RAM中发送缓冲区的起始地址为SPSTART(0x40) */
outportb(RCR, 0xcc); /* 设置Rtl8019的接收配置值为0xcc。意义如下:
1.将接收的数据包缓存到内存;
2.进行MAC地址比较;
3.支持多点传送;
4.不接收小于64b的包;
5.不接收发生传输错误的包。*/
outportb(TCR, 0xe0); /* 设置Rtl8019的接收配置值为0xe0。意义如下:
1.启用传输冲突的抵消功能;
2.禁用常规传输的自动发送功能;
3.设置传输方式为常规模式;
4.启用CRC校验功能。*/
#ifdef RTL8019_OP_16 /* 该宏用来标志是否进行的是16位操作 */
outportb(DCR, 0xc9); /* 设置Rtl8019的数据配置为0xc9, 16位的DMA */
#else
outportb(DCR, 0xc8); /* 设置Rtl8019的数据配置为0xc8, 8位的DMA */
#endif
outportb(IMR, 0x03); /* 设置Rtl8019的中断掩码位0x03,即启用接收和发送中断 */
outportb(ISR, 0xff); /* 清空Rtl8019的中断状态寄存器 */
SetRegPage(1); /* 切换到Rtl8019寄存器表的页1 */
for(i=0; i<6; i++) /* 将设备的MAC地址输入到Page1中的PAR0-5寄存器中 */
#ifdef RTL8019_OP_6
outportb(BaseAddr+(1+i)*2, dev->dev_addr[i]);
#else
outportb(BaseAddr+(1+i), dev->dev_addr[i]);
#endif
outportb(CURR, RPSTART+1); /* 写页地址寄存器。该寄存器存储的是用于放置下一个数据包的起始页地址。
这里将它设置为缓冲区的起始页地址加1,表示当前没有收到任何数据。*/
outportb(MAR0, 0x00); /* 设置在Hash校验时用于对多点传送地址进行过滤的位值 */
outportb(MAR1, 0x41);
outportb(MAR2, 0x00);
outportb(MAR3, 0x80);
outportb(MAR4, 0x00);
outportb(MAR5, 0x00);
outportb(MAR6, 0x00);
outportb(MAR7, 0x00);
outportb(BaseAddr, 0x22); /* 切换到Rtl8019寄存器表的页0并执行Rtl8019的start命令,准备进行数据传输 */
rBNRY = RPSTART; /* 将Rtl8019接收缓冲区中的当前读取地址(即起始地址)保存备用 */
enable_irq(dev->irq); /* 启用设备的中断功能 */
/* 开启设备的数据传输队列(即从此允许设备进行收发数据包) */
netif_start_queue(dev);
return 0;
}
/***** 设备的发送函数 *****/
static int nic_8019_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
int i;
#ifdef RTL8019_OP_16
u16 len,TxLen;
u16 *data;
#else
int len, TxLen; /* 向Rtl8019的RAM写入的数据长度及Rtl8019向外发送的数据长度 */
u8 *data; /* 数据缓冲区 */
#endif
struct nic_8019_priv *priv = (struct nic_8019_priv *) dev->priv; /* 获得设备的私有结构体 */
TRACE("start_xmit\n"); /* 输出跟踪信息 */
len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len; /* 获取数据包的长度,如果该长度小于最小长度,就等于最小长度 */
TRACE("\nTx Length = %i,%x,%x\n", len, skb->data[12], skb->data[13]); /* 输出设备报头的最后两位(即类型字段) */
#ifdef RTL8019_OP_16
data =(u16*) skb->data;
#else
data = (u8*) skb->data; /* 获取数据包中的数据并进行类型转换 */
#endif
/***** 主机CPU先以远程DMA方式将数据写入到Rtl8019的RAM中 *****/
outportb(BaseAddr,0x22); /* 切换到Rtl8019寄存器表的页0并中止远程DMA传输 */
if (inportb(BaseAddr)&4) /* 如果最后一次远程DMA操作没有完成,则返回1表示设备繁忙的错误,然后重新执行该函数 */
return 1;
#ifdef bug_fix_for_write
//read page 42,0,42,0 before write if you have problem
#endif
outportb(RSAR0, 0); /* 设置远程DMA传输的起始地址为SPSTART(0x40)。先打入低8位,*/
outportb(RSAR1, SPSTART); /* 后打入高8位 */
outportb(RBCR0, len&0xff); /* 设置远程DMA传输的数据长度。先打入低8位,*/
outportb(RBCR1, len>>8); /* 后打入高8位 */
outportb(BaseAddr, 0x12); /* 开始远程DMA写入操作 */
dev->trans_start = jiffies; /* 记录此次发送操作的开始时间 */
#ifdef RTL8019_OP_16
TxLen=(len+1)/2; /* 如果是16位DMA操作则将长度减半 */
#else
TxLen=len; /* 否则直接获得数据长度 */
#endif
for(i=0; i<TxLen; i++) {
#ifdef RTL8019_OP_16
outportw(RWPORT, data[i]);
TRACE("%2X,%2X,",data[i]&0xff,data[i]>>8);
#else
outportb(RWPORT, data[i]); /* 通过Rtl8019的Remote DMA Port将数据包内的数据拷贝到Rtl8019的RAM中 */
TRACE("%2X,",skb->data[i]); /* 同时将数据作为跟踪信息输出以便查阅 */
#endif
}
TRACE("\n");
while(inportb(BaseAddr)&4); /* 等待传输操作完成 */
/***** 然后Rtl8019再将自己RAM中的数据发送出去 *****/
outportb(TPSR, SPSTART); /* 设置Rtl8019的RAM中要发送数据包的起始地址为SPSTART(0x40) */
outportb(TBCR0, len&0xff); /* 设置要传输的数据包长度。先打入低8位, */
outportb(TBCR1, len>>8); /* 后打入高8位, */
outportb(BaseAddr, 0x1e); /* 开始发送数据包 */
dev_kfree_skb(skb); /* 释放Socket缓冲区 */
return 0;
}
/***** 设备的中断响应函数 *****/
irqreturn_t nic_8019_rx(int irq, void *dev_id, struct pt_regs *regs)
{
u8 RxPageBeg, RxPageEnd; /* 接收缓冲区内数据的起始地址和结束地址 */
u8 RxNextPage; /* 下一个数据页的起始地址 */
u8 RxStatus; /* 中断状态 */
#ifdef RTL8019_OP_16
u16 *data,temp;
u16 i, RxLength,RxLen;
#else
u8* data; /* 数据缓冲区 */
int i, RxLength, RxLen; /* 数据总长度及已获得的长度 */
#endif
struct sk_buff *skb; /* 定义Socket缓冲区 */
struct net_device *dev = (struct net_device *) dev_id; /* 获得设备对象 */
struct nic_8019_priv *priv = (struct nic_8019_priv *) dev->priv; /* 获得设备的私有结构体 */
TRACE("TX/RX Interupt!\n"); /* 输出跟踪信息 */
spin_lock(&priv->lock); /* 获得自旋锁 */
SetRegPage(0); /* 切换到Rtl8019寄存器表的页0 */
outportb(BNRY, rBNRY); /* 将边界寄存器的值设置为接收缓冲区的起始地址(rBNRY在open函数中以赋值) */
RxStatus = inportb(ISR); /* 获得引发此次中断的操作类型 */
/***** 若操作类型为发送操作 *****/
if (RxStatus & 2) {
outportb(ISR, 0x2); /* 清空中断状态寄存器中的发送状态位 */
priv->stats.tx_packets++; /* 累加设备私有结构体中的已接收数据包的个数*/
TRACE("transmit one packet complete!\n"); /* 输出跟踪信息 */
}
/***** 若操作类型为接收操作 *****/
if (RxStatus & 1) {
TRACE("Receivex packet....\n"); /* 输出跟踪信息 */
outportb(ISR, 0x1); /* 清空中断状态寄存器中的接收状态位 */
SetRegPage(1); /* 切换到Rtl8019寄存器表的页1 */
RxPageEnd = inportb(CURR); /* 将页结束地址初始化为下一个数据页的起始地址 */
SetRegPage(0); /* 切换到Rtl8019寄存器表的页0 */
RxPageBeg = rBNRY+1; /* 将页起始地址初始化为边界地址加1 */
if(RxPageBeg>=RPSTOP) /* 如果起始地址大于或等于接收缓冲区的结束地址,*/
RxPageBeg = RPSTART; /* 那么就将它设置为接收缓冲区的起始地址 */
outportb(BaseAddr, 0x22); /* 中止远程DMA操作 */
/***** 主机CPU从Rtl8019的RAM中将数据包的前4位读出,这4位标志是Rtl8019单独添加的,
通过其中的长度位来判断本次封包的长度是否合法 *****/
//outport(RSAR0, RxPageBeg<<8);
//outport(RBCR0, 256);
outportb(RSAR0, 0); /* 设置远程DMA传输的读取地址 */
outportb(RSAR1, RxPageBeg);
outportb(RBCR0, 4); /* 设置远程DMA传输的数据长度 */
outportb(RBCR1, 0);
outportb(BaseAddr, 0xa); /* 执行Rtl8019的start命令起动远程DMA读取操作 */
#ifdef RTL8019_OP_16
temp = inportw(RWPORT);
RxNextPage = temp>>8;
RxStatus = temp&0xff;
RxLength = inportw(RWPORT);
#else /* 获得前4个字节 */
RxStatus = inportb(RWPORT); /* 获得接收状态 */
RxNextPage = inportb(RWPORT); /* 获得下一数据页的起始地址 */
RxLength = inportb(RWPORT); /* 获得整个数据包长度 */
RxLength |= inportb(RWPORT)<<8;
#endif
TRACE("\nRxBeg = %x, RxEnd = %x, nextpage = %x, size = %i\n", RxPageBeg, RxPageEnd, RxNextPage, RxLength);
if(RxLength == 0){ /* 如果数据长度为0,则输出调试信息并返回IRQ_HANDLED表示已有中断被触发 */
TRACE("Rxlength == 0\n");
return IRQ_HANDLED;
}
RxLength -= 4; /* 获得实际有效数据的长度 */
if (RxLength>ETH_FRAME_LEN) { /* 如果数据长度大于封包最大长度 */
if (RxPageEnd==RPSTART) /* 如果接收数据的结束页地址等于缓冲区的起始地址,那么说明所有数据已被读取, */
rBNRY = RPSTOP-1; /* 则将边界地址设置为缓冲区的结束页地址减1,表示已经读取到缓冲区尾部。*/
else
rBNRY = RxPageEnd-1; /* 否则将边界地址设置为接收数据的结束页地址减1 */
outportb(BNRY, rBNRY); /* 设置边界寄存器的值 */
TRACE("RxLength more long than %x\n", ETH_FRAME_LEN); /* 输出数据过长的跟踪信息 */
return IRQ_HANDLED; /* 返回IRQ_HANDLED表示已有中断被触发 */
}
#ifdef RTL8019_OP_16
skb = dev_alloc_skb(RxLength+2);
#else
skb = dev_alloc_skb(RxLength); /* 为Socket缓冲区分配空间 */
#endif
if (!skb) { /* 如果分配失败 */
TRACE("Rtl8019as eth: low on mem - packet dropped\n"); /* 输出失败的跟踪信息 */
priv->stats.rx_dropped++; /* 累加设备私有结构体中的丢掉的数据包计数 */
return IRQ_HANDLED; /* 返回IRQ_HANDLED表示已有中断被触发 */
}
skb->dev = dev; /* 设置Socket缓冲区中的设备句柄 */
skb_reserve(skb, 2); /* 在Socket缓冲区的前面预留2个字节的空间 */
skb_put(skb, RxLength); /* 在Socket缓冲区内为接收数据分配空间并更新缓冲区的tail和len成员 */
#ifdef RTL8019_OP_16
data = ( u16 *)skb->data;
#else
data = ( u8 *)skb->data; /* 获取Socket缓冲区内的数据起始地址 */
#endif
// eth_copy_and_sum(skb, data, len, 0);
outportb(RSAR0, 4); /* 设置远程DMA传输的读取地址 */
outportb(RSAR1, RxPageBeg);
outportb(RBCR0, RxLength); /* 设置远程DMA传输的数据长度 */
outportb(RBCR1, RxLength>>8);
outportb(BaseAddr, 0xa); /* 执行Rtl8019的start命令起动远程读取操作 */
#ifdef RTL8019_OP_16
i = 2;
data -= 2;
RxLen=(RxLength+1)/2;
#else
i = 4;
data -= 4; /* 将数据缓冲区的地址提前4个字节,以便获取实际数据 */
RxLen=RxLength; /* 获取接收的数据长度 */
#endif
for(; RxLen--;) {
#ifdef RTL8019_OP_16
static const int cmp_val = 0x7f;
#else
static const int cmp_val = 0xff;
#endif
if (!(i & cmp_val)) {
outportb(BNRY, RxPageBeg);
RxPageBeg++;
if(RxPageBeg>=RPSTOP)
RxPageBeg = RPSTART;
}
#ifdef RTL8019_OP_16
data[i++] = inportw(RWPORT);
TRACE("%2X,%2X,", data[i-1]&0xff,data[i-1]>>8);
#else
data[i++] = inportb(RWPORT); /* 从Rtl8019的Remote DMA port读取数据 */
TRACE("%2X,", data[i-1]); /* 将数据作为跟踪信息输出 */
#endif
}
TRACE("\n");
outportb(BNRY, RxPageBeg); /* 设置边界寄存器的值 */
rBNRY = RxPageBeg; /* 保存当前读页地址 */
skb->protocol = eth_type_trans(skb, dev);/* 设置skb->pkt_type用来判断该包是否属于该主机,并返回一个协议编号 */
TRACE("\nprotocol=%x\n", skb->protocol); /* 输出协议编号 */
priv->stats.rx_packets++; /* 累计接收到的数据包计数 */
priv->stats.rx_bytes +=RxLength; /* 累计接收到的字节计数 */
netif_rx(skb); /* 如果skb->pkt_type为PACKET_HOST,则通知内核已经收到新的数据包,
如果为PACKET_OTHERHOST,则丢掉该包。*/
} else {
outportb(ISR, 0xfe); /* 清空Rtl8019的中断状态寄存器(接收状态位除外) */
}
spin_unlock(&priv->lock); /* 释放自旋锁 */
return IRQ_HANDLED;
}
/***** 获得设备的统计信息 *****/
static struct net_device_stats *nic_8019_get_stats(struct net_device *dev)
{
struct nic_8019_priv *priv = (struct nic_8019_priv *) dev->priv; /* 获得设备私有结构体的指针 */
TRACE("get_stats\n"); /* 输出跟踪信息 */
return &priv->stats; /* 返回设备的统计信息 */
}
/***** 设备的终止函数 *****/
static int nic_8019_stop(struct net_device *dev)
{
TRACE("stop\n"); /* 输出跟踪信息 */
SetRegPage(3); /* 切换到Rtl8019寄存器表的页3 */
outportb(CR9346, 0xcf); /* 将9346CR寄存器的EEM1和EEM0都设置为1,以便启用配置寄存器CONFIG3的写入功能 */
outportb(CONFIG3, 0x66); /* 进入Rtl8019的sleep(休眠)和pwrdn(低功耗)模式, 将led0设置为led_col(响应传输冲突),
将led1设置为led_crs(响应数据传输,包括发送和接收)。*/
outportb(CR9346, 0x3f); /* 禁用配置寄存器CONFIG3的写入功能 */
free_irq(dev->irq, dev); /* 释放设备中断 */
netif_stop_queue(dev); /* 停止设备的传输队列 */
MOD_DEC_USE_COUNT; /* 递减该模块的调用次数 */
return 0;
}
/***** 模块的清理函数 *****/
void __exit nic_8019_cleanup(void)
{
TRACE("cleanup\n"); /* 输出跟踪信息 */
kfree(nic_8019_netdevs.priv); /* 释放设备的私有结构体 */
unregister_netdev(&nic_8019_netdevs); /* 注销设备 */
return;
}
/* 指定模块的初始化和清理函数 */
module_init(nic_8019_init_module);
module_exit(nic_8019_cleanup);
/* 设置模块信息 */
MODULE_DESCRIPTION("Rtl8019as ethernet driver");
MODULE_AUTHOR("antiscle <[email protected]>");
MODULE_LICENSE("GPL");
相关阅读 更多
热门阅读
-
office 2019专业增强版最新2021版激活秘钥/序列号/激活码推荐 附激活工具
阅读:74
-
如何安装mysql8.0
阅读:31
-
Word快速设置标题样式步骤详解
阅读:28
-
20+道必知必会的Vue面试题(附答案解析)
阅读:37
-
HTML如何制作表单
阅读:22
-
百词斩可以改天数吗?当然可以,4个步骤轻松修改天数!
阅读:31
-
ET文件格式和XLS格式文件之间如何转化?
阅读:24
-
react和vue的区别及优缺点是什么
阅读:121
-
支付宝人脸识别如何关闭?
阅读:21
-
腾讯微云怎么修改照片或视频备份路径?
阅读:28