网卡驱动程序
如果SOC不支持网络,指的是没有内置的MAC,需要外置MAC和PHY芯片,如:DM9000对SOC提供一个SRAM接口,SOC可以以SRAM方式操作DM9000。有些外置的网络芯片更强大,内部甚至集成了硬件 TCP/IP 协议栈,对外提供一个 SPI 接
口,比如 W5500。这个一般用于单片机领域,单片机通过 SPI 接口与 W5500 进行通信,由于W5500 内置了硬件 TCP/IP 协议栈,因此单片机就不需要移植负责的软件协议栈,直接通过 SPI来操作 W5500,简化了单片机联网方案 。
有些SCO集成MAC,但是需要外接PHY芯片。
①、内部 MAC 外设会有专用的加速模块,比如专用的 DMA,加速网速数据的处理。
②、网速快,可以支持 10/100/1000M 网速。
③、外接 PHY 可选择性多,成本低。
内部的 MAC 外设会通过 MII 或者 RMII 接口来连接外部的 PHY 芯片, MII/RMII 接口用来传输网络数据。另外主控需要配置或读取 PHY 芯片,也就是读写 PHY 的内部寄存器,所以还
需要一个控制接口,叫做 MIDO, MDIO 很类似 IIC,也是两根线,一根数据线叫做 MDIO,一根时钟线叫做 MDC。

接口
(1)MII 接口
MII 全称是 Media Independent Interface,直译过来就是介质独立接口,它是 IEEE-802.3 定义的以太网标准接口, MII 接口用于以太网 MAC 连接 PHY 芯片,连接示意图如图 69.1.2.1 所示 
MII 接口一共有 16 根信号线,含义如下:
TX_CLK: 发送时钟,如果网速为 100M 的话时钟频率为 25MHz, 10M 网速的话时钟频率
为 2.5MHz,此时钟由 PHY 产生并发送给 MAC。
TX_EN: 发送使能信号。
TX_ER: 发送错误信号,高电平有效,表示 TX_ER 有效期内传输的数据无效。 10Mpbs 网
速下 TX_ER 不起作用。
TXD[3:0]:发送数据信号线,一共 4 根。
RXD[3:0]: 接收数据信号线,一共 4 根。
RX_CLK: 接收时钟信号,如果网速为 100M 的话时钟频率为 25MHz, 10M 网速的话时钟
频率为 2.5MHz, RX_CLK 也是由 PHY 产生的。
RX_ER: 接收错误信号,高电平有效,表示 RX_ER 有效期内传输的数据无效。 10Mpbs 网
速下 RX_ER 不起作用。
RX_DV: 接收数据有效,作用类似 TX_EN。
CRS: 载波侦听信号。
COL: 冲突检测信号。
(2)RMII 接口
RMII 全称是 Reduced Media Independent Interface,翻译过来就是精简的介质独立接口,也就是 MII 接口的精简版本。 RMII 接口只需要 7 根数据线,相比 MII 直接减少了 9 根,极大的方便了板子布线

TX_EN: 发送使能信号。
TXD[1:0]: 发送数据信号线,一共 2 根。
RXD[1:0]:接收数据信号线,一共 2 根。
CRS_DV: 相当于 MII 接口中的 RX_DV 和 CRS 这两个信号的混合。
REF_CLK: 参考时钟,由外部时钟源提供, 频率为 50MHz。这里与 MII 不同, MII 的接收和发送时钟是独立分开的,而且都是由 PHY 芯片提供的。
(3)MDIO 接口
MDIO 全称是 Management Data Input/Output,直译过来就是管理数据输入输出接口,是一
个简单的两线串行接口,一根 MDIO 数据线,一根 MDC 时钟线。驱动程序可以通过 MDIO 和
MDC 这两根线访问 PHY 芯片的任意一个寄存器。 MDIO 接口支持多达 32 个 PHY。同一时刻
内只能对一个 PHY 进行操作,那么如何区分这 32 个 PHY 芯片呢?和 IIC 一样,使用器件地址
即可。同一 MDIO 接口下的所有 PHY 芯片,其器件地址不能冲突,必须保证唯一 。
(4)RJ45 接口
网络设备是通过网线连接起来的,插入网线的叫做 RJ45 座,RJ45 座要与 PHY 芯片连接在一起,但是中间需要一个网络变压器,网络变压器用于隔离
以及滤波等


网卡将上层协议传递下来的数据包,以特定的媒介访问控制方式进行发送,并将接收到的数据包传送给上层协议。
网卡设备与字符设备和块设备不同,不会对应于/dev目录下的文件,不过会存放在/sys/class/net目录下,可以通过ifconfig或者ls /sys/class/net查看,lo代表回环,接受和发送不同接口

发送数据时,写控制寄存器TXCMD,将发送数据长度写入TXLENG,将数据写入PORT0。
一、网络基础知识
(1)、协议栈对比
Linux的优点之一在于它丰富而稳定的网络协议栈。其范围从协议无关层(例如通用 socket 层接口或设备层)到各种具体的网络协议实现。对于网络的理论介绍一般都采用 OSI(Open Systems Interconnection)模型,但是Linux 中网络栈的介绍一般分为四层的Internet 模型。
1)网络协议接口层:
实现统一的数据包收发的协议,该层主要负责调用dev_queue_xmit()函数发送数据, netif_rx()函数接收数据
2)网络设备接口层:
通过net_device结构体来描述一个具体的网络设备的信息,实现不同的硬件的统一
3)设备驱动功能层:
用来负责驱动网络设备硬件来完成各个功能, 它通过hard_start_xmit() 函数启动发送操作, 并通过网络设备上的中断触发接收操作,
4)网络设备与媒介层:
用来负责完成数据包发送和接收的物理实体, 设备驱动功能层的函数都在这物理上驱动
Intel(R) Ethernet Connection (7) I219-LM
Advanced Micro Devices, Inc. [AMD] 79c970 [PCnet32 LANCE] (rev 10)

网络接口层
网络接口层把数据链路层和物理层合并在了一起,提供访问物理设备的驱动程序,对应的网络协议主要是以太网协议
网际层
网络层协议管理离散的计算机间的数据传输,如IP协议为用户和远程计算机提供了信息包的传输方法,确保信息包能正确地到达目的机器。重要的网络层协议包括ARP(地址解析协议)、ICMP(Internet控制消息协议)和IP协议(网际协议)等
传输层
传输层的功能包括:格式化信息流、提供可靠传输。传输层包括TCP(Transmission Control Protocol,传输控制协议)和UDP(User Datagram Protocol,用户数据报协议),它们是传输层中最主要的协议。
应用层
应用层位于协议栈的顶端,它的主要任务是服务于应用,如利用FTP(文件传输协议)传输一个文件。常见的应用层协议有:HTTP,FTP,Telnet等。应用层是Linux网络设定很关键的一层,Linux服务器的配置文档主要针对应用层中的协议
(2) 设备描述
struct net_device { /* 设备名称,如eth0 */ char name[IFNAMSIZ]; /* 名称hash */ struct hlist_node name_hlist; char *ifalias; /* * I/O specific fields * FIXME: Merge these and struct ifmap into one */ /* 描述设备所用的共享内存,用于设备与内核沟通 其初始化和访问只会在设备驱动程序内进行 */ unsigned long mem_end; unsigned long mem_start; /* 设备自有内存映射到I/O内存的起始地址 */ unsigned long base_addr; /* 设备与内核对话的中断编号,此值可由多个设备共享 驱动程序使用request_irq函数分配此变量,使用free_irq予以释放 */ int irq; /* 侦测网络状态的改变次数 */ atomic_t carrier_changes; /* * Some hardware also needs these fields (state,dev_list, * napi_list,unreg_list,close_list) but they are not * part of the usual set specified in Space.c. */ /* 网络队列子系统使用的一组标识 由__LINK_STATE_xxx标识 */ unsigned long state; struct list_head dev_list; struct list_head napi_list; struct list_head unreg_list; struct list_head close_list; /* 当前设备所有协议的链表 */ struct list_head ptype_all; /* 当前设备特定协议的链表 */ struct list_head ptype_specific; struct { struct list_head upper; struct list_head lower; } adj_list; /* 用于存在其他一些设备功能 可报告适配卡的功能,以便与CPU通信 使用NETIF_F_XXX标识功能特性 */ netdev_features_t features; netdev_features_t hw_features; netdev_features_t wanted_features; netdev_features_t vlan_features; netdev_features_t hw_enc_features; netdev_features_t mpls_features; netdev_features_t gso_partial_features; /* 网络设备索引号 */ int ifindex; /* 设备组,默认都属于0组 */ int group; struct net_device_stats stats; atomic_long_t rx_dropped; atomic_long_t tx_dropped; atomic_long_t rx_nohandler; #ifdef CONFIG_WIRELESS_EXT const struct iw_handler_def *wireless_handlers; struct iw_public_data *wireless_data; #endif /* 设备操作接口 */ const struct net_device_ops *netdev_ops; /* ethtool操作接口 */ const struct ethtool_ops *ethtool_ops; #ifdef CONFIG_NET_SWITCHDEV const struct switchdev_ops *switchdev_ops; #endif #ifdef CONFIG_NET_L3_MASTER_DEV const struct l3mdev_ops *l3mdev_ops; #endif #if IS_ENABLED(CONFIG_IPV6) const struct ndisc_ops *ndisc_ops; #endif #ifdef CONFIG_XFRM const struct xfrmdev_ops *xfrmdev_ops; #endif /* 头部一些操作,如链路层缓存,校验等 */ const struct header_ops *header_ops; /* 标识接口特性,IFF_XXX,如IFF_UP */ unsigned int flags; /* 用于存储用户空间不可见的标识 由VLAN和Bridge虚拟设备使用 */ unsigned int priv_flags; /* 几乎不使用,为了兼容保留 */ unsigned short gflags; /* 结构对齐填充 */ unsigned short padded; /* 与interface group mib中的IfOperStatus相关 */ unsigned char operstate; unsigned char link_mode; /* 接口使用的端口类型 */ unsigned char if_port; /* 设备使用的DMA通道 并非所有设备都可以用DMA,有些总线不支持DMA */ unsigned char dma; /* 最大传输单元,标识设备能处理帧的最大尺寸 Ethernet-1500 */ unsigned int mtu; /* 最小mtu,Ethernet-68 */ unsigned int min_mtu; /* 最大mut,Ethernet-65535 */ unsigned int max_mtu; /* 设备所属类型 ARP模块中,用type判断接口的硬件地址类型 以太网接口为ARPHRD_ETHER */ unsigned short type; /* 设备头部长度 Ethernet报头是ETH_HLEN=14字节 */ unsigned short hard_header_len; unsigned char min_header_len; /* 必须的头部空间 */ unsigned short needed_headroom; unsigned short needed_tailroom; /* Interface address info. */ /* 硬件地址,通常在初始化过程中从硬件读取 */ unsigned char perm_addr[MAX_ADDR_LEN]; unsigned char addr_assign_type; /* 硬件地址长度 */ unsigned char addr_len; unsigned short neigh_priv_len; unsigned short dev_id; unsigned short dev_port; spinlock_t addr_list_lock; /* 设备名赋值类型,如NET_NAME_UNKNOWN */ unsigned char name_assign_type; bool uc_promisc; struct netdev_hw_addr_list uc; struct netdev_hw_addr_list mc; struct netdev_hw_addr_list dev_addrs; #ifdef CONFIG_SYSFS struct kset *queues_kset; #endif /* 混杂模式开启数量 */ unsigned int promiscuity; /* 非零值时,设备监听所有多播地址 */ unsigned int allmulti; /* Protocol-specific pointers */ /* 特定协议的指针 */ #if IS_ENABLED(CONFIG_VLAN_8021Q) struct vlan_info __rcu *vlan_info; #endif #if IS_ENABLED(CONFIG_NET_DSA) struct dsa_switch_tree *dsa_ptr; #endif #if IS_ENABLED(CONFIG_TIPC) struct tipc_bearer __rcu *tipc_ptr; #endif void *atalk_ptr; /* ip指向in_device结构 */ struct in_device __rcu *ip_ptr; struct dn_dev __rcu *dn_ptr; struct inet6_dev __rcu *ip6_ptr; void *ax25_ptr; struct wireless_dev *ieee80211_ptr; struct wpan_dev *ieee802154_ptr; #if IS_ENABLED(CONFIG_MPLS_ROUTING) struct mpls_dev __rcu *mpls_ptr; #endif /* * Cache lines mostly used on receive path (including eth_type_trans()) */ /* Interface address info used in eth_type_trans() */ unsigned char *dev_addr; #ifdef CONFIG_SYSFS /* 接收队列 */ struct netdev_rx_queue *_rx; /* 接收队列数 */ unsigned int num_rx_queues; unsigned int real_num_rx_queues; #endif struct bpf_prog __rcu *xdp_prog; unsigned long gro_flush_timeout; /* 如网桥等的收包回调 */ rx_handler_func_t __rcu *rx_handler; /* 回调参数 */ void __rcu *rx_handler_data; #ifdef CONFIG_NET_CLS_ACT struct tcf_proto __rcu *ingress_cl_list; #endif struct netdev_queue __rcu *ingress_queue; #ifdef CONFIG_NETFILTER_INGRESS /* netfilter入口 */ struct nf_hook_entry __rcu *nf_hooks_ingress; #endif /* 链路层广播地址 */ unsigned char broadcast[MAX_ADDR_LEN]; #ifdef CONFIG_RFS_ACCEL struct cpu_rmap *rx_cpu_rmap; #endif /* 接口索引hash */ struct hlist_node index_hlist; /* * Cache lines mostly used on transmit path */ /* 发送队列 */ struct netdev_queue *_tx ____cacheline_aligned_in_smp; /* 发送队列数 */ unsigned int num_tx_queues; unsigned int real_num_tx_queues; /* 排队规则 */ struct Qdisc *qdisc; #ifdef CONFIG_NET_SCHED DECLARE_HASHTABLE (qdisc_hash, 4); #endif /* 可在设备发送队列中排队的最大数据包数 */ unsigned long tx_queue_len; spinlock_t tx_global_lock; /* 网络层确定传输超时, 调用驱动程序tx_timeout接口的最短时间 */ int watchdog_timeo; #ifdef CONFIG_XPS struct xps_dev_maps __rcu *xps_maps; #endif #ifdef CONFIG_NET_CLS_ACT struct tcf_proto __rcu *egress_cl_list; #endif /* These may be needed for future network-power-down code. */ /* watchdog定时器 */ struct timer_list watchdog_timer; /* 引用计数 */ int __percpu *pcpu_refcnt; /* 网络设备的注册和除名以两步进行, 该字段用于处理第二步 */ struct list_head todo_list; struct list_head link_watch_list; /* 设备的注册状态 */ enum { NETREG_UNINITIALIZED=0, NETREG_REGISTERED, /* completed register_netdevice */ NETREG_UNREGISTERING, /* called unregister_netdevice */ NETREG_UNREGISTERED, /* completed unregister todo */ NETREG_RELEASED, /* called free_netdev */ NETREG_DUMMY, /* dummy device for NAPI poll */ } reg_state:8; /* 设备要被释放标记 */ bool dismantle; enum { RTNL_LINK_INITIALIZED, RTNL_LINK_INITIALIZING, } rtnl_link_state:16; bool needs_free_netdev; void (*priv_destructor)(struct net_device *dev); #ifdef CONFIG_NETPOLL struct netpoll_info __rcu *npinfo; #endif possible_net_t nd_net; /* mid-layer private */ union { void *ml_priv; struct pcpu_lstats __percpu *lstats; struct pcpu_sw_netstats __percpu *tstats; struct pcpu_dstats __percpu *dstats; struct pcpu_vstats __percpu *vstats; }; #if IS_ENABLED(CONFIG_GARP) struct garp_port __rcu *garp_port; #endif #if IS_ENABLED(CONFIG_MRP) struct mrp_port __rcu *mrp_port; #endif struct device dev; const struct attribute_group *sysfs_groups[4]; const struct attribute_group *sysfs_rx_queue_group; const struct rtnl_link_ops *rtnl_link_ops; /* for setting kernel sock attribute on TCP connection setup */ #define GSO_MAX_SIZE 65536 unsigned int gso_max_size; #define GSO_MAX_SEGS 65535 u16 gso_max_segs; #ifdef CONFIG_DCB const struct dcbnl_rtnl_ops *dcbnl_ops; #endif u8 num_tc; struct netdev_tc_txq tc_to_txq[TC_MAX_QUEUE]; u8 prio_tc_map[TC_BITMASK + 1]; #if IS_ENABLED(CONFIG_FCOE) unsigned int fcoe_ddp_xid; #endif #if IS_ENABLED(CONFIG_CGROUP_NET_PRIO) struct netprio_map __rcu *priomap; #endif struct phy_device *phydev; struct lock_class_key *qdisc_tx_busylock; struct lock_class_key *qdisc_running_key; bool proto_down; };
主要成员:
char name[IFNAMSIZ]:设备名,eth%d
unsigned long state:设备状态
unsigned long base_addr:I/O基地址
unsigned intirq:中断号
int (*init)(struct net_device *dev):初始化网卡,主要有以下步骤
a、在register_netdev时被调用对net_device进行初始化,
b、设置网卡硬件相关的寄存器
c、设置net_device结构体的成员
d、使用register_netdev()来注册net_device结构体
申请net_device
net_device结构可以使用如下进行内核函数动态分配
1、struct net_device *alloc_netdev(int sizeof_priv, const char *mask, void(*setup)(struct net_device *))
sizeof_priv 私有数据区大小; mask设备名; setup初始化函数
2、struct net_device *alloc_ethnetdev(int sizeof_priv)
注册设备
int register_netdevice(struct net_device *dev)
调用者首先填写 net_device 结构,设备驱动程序可能会通过调用 register_netdevice 在内核中进行注册,内核调用它的 init 函数,然后执行一组健全性检查,并将新设备添加到设备列表中(内核中的活动设备链表)。
要从协议层向设备发送数据,需要使用dev_queue_xmit函数,这个函数对数据进行排队,并交由底层设备驱动程序进行最终传输报文的接收通常是使用netif_rx 执行的。当底层设备驱动程序接收到一个报文(包含在所分配的sk_buff中)时,就会通过调用 netif_rx 将 数据上传至设备无关层,然后,这个函数通过 netif_rx_schedule将 sk_buff 在上层协议队列中进行排队,供以后进行处理。
net_device 有个非常重要的成员变量: netdev_ops,为 net_device_ops 结构体指针类型,这就是网络设备的操作集
参照https://www.cnblogs.com/11ys/articles/14103666.html
sk_buff:
参照https://www.cnblogs.com/tzh36/p/5424564.html
NAPI(轮询或中断 )
中断的好处就是响应快,数据量小的时候处理及时,速度快,但是一旦当数据量大,而且都是短帧的时候会导致中断频繁发生,消耗大量的 CPU 处理时间在中断自身处理上。轮询恰好相反,响应没有中断及时,但是在处理大量数据的时候不需要消耗过多的 CPU 处理时间。
NAPI 的核心思想就是不全部采用中断来读取网络数据,而是采用中断来唤醒数据接收服务程序,在接收服务程序中采用 POLL 的方法来轮询处理数据。
(1)初始化NAPI
void netif_napi_add(struct net_device *dev, struct napi_struct *napi, int (*poll)(struct napi_struct *, int), int weight)
dev: 每个 NAPI 必须关联一个网络设备,此参数指定 NAPI 要关联的网络设备。
napi:要初始化的 NAPI 实例。
poll: NAPI 所使用的轮询函数,非常重要,一般在此轮询函数中完成网络数据接收的工作。
weight: NAPI 默认权重(weight),一般为 NAPI_POLL_WEIGHT。
返回值: 无。
(2)删除 NAPI
void netif_napi_del(struct napi_struct *napi)
napi: 要删除的 NAPI。
返回值: 无
(3)使能NAPI
inline void napi_enable(struct napi_struct *n)
(4)关闭NAPI
void napi_disable(struct napi_struct *n)
(5)检查 NAPI 是否可以进行调度
inline bool napi_schedule_prep(struct napi_struct *n)
(6)调度NAPI
如果可以调度的话就进行调度,使用__napi_schedule 函数完成 NAPI 调度
void __napi_schedule(struct napi_struct *n)
(7)处理完成
NAPI 处理完成以后需要调用 napi_complete 函数来标记 NAPI 处理完成
inline void napi_complete(struct napi_struct *n)
打开设备
通过opne函数请求需要的系统资源
注册中断、DMA等
设置寄存器,启动设备
启动发送队列
int net_open(struct net_device *dev) { /*申请中断*/ request_irq(dev->irq, &net_interrupt, SA_SHIRQ,“dm9000”, dev); /* 设置寄存器,启动设备 */ ...... ...... ...... ...... /*启动发送队列*/ netif_start_queue(dev); }
发送数据
当核心需要发送一个数据包时,它调用hard_start_transmit函数,该函数将最终调用到net_device结构中的hard_start_xmit函数指针。
int (*hard_start_xmit) (struct sk_buff *skb, struct net_device *dev);
- 1)把数据包发出去之前,需要使用netif_stop_queue()来停止上层传下来的数据包,
- 2)设置寄存器,通过网络设备硬件,来发送数据
- 3)当数据包发出去后, 再调用dev_kfree_skb()函数来释放sk_buff,该函数原型如下:
- 4)当数据包发出成功,就会进入TX中断函数,然后更新统计信息,调用netif_wake_queue()来唤醒,启动上层继续发包下来.
- 5)若数据包发出去超时,一直进不到TX中断函数,就会调用net_device结构体的(*tx_timeout)超时成员函数,在该函数中更新统计信息, 调用netif_wake_queue()来唤醒
void dev_kfree_skb(struct sk_buff *skb); void netif_wake_queue(struct net_device *dev); //唤醒被阻塞的上层,启动继续向网络设备驱动层发送数据包 void netif_stop_queue(struct net_device *dev); //阻止上层向网络设备驱动层发送数据包
接受数据
网络接口驱动可以实现两种方式的报文接收:
中断和轮询,Linux中驱动多采用中断方式。BootLoader一般采用轮询
1、分配Skb
skb = dev_alloc_skb(pkt->datalen + 2)
2、从硬件中读取数据到Skb
3、调用设备无关接口netif_rx将数据交给协议栈
netif_rx(skb)
中断处理
网络接口通常支持3种类型的中断: 新报文到达中断、报文发送完成中断和出错中断。中断处理程序可通过查看网卡中的中断状态寄存器,来分辨出中断类型
三、以CS89X0为例
发送数据时,写控制寄存器TXCMD,将发送数据的长度写入TXLENG,然后将数据依次写入PORT0口,网卡芯片将数据组织为链路层类型并添加填充位和CRC校验送到网络。
接受数据,数据到达后,网卡产生中断,通过PORT0寄存器可以将数据从网卡读出来。
1.模块注册
static int __init init_cs8900a_s3c2410(void) { struct net_local *lp; int ret = 0;
dev_cs89x0.irq = irq; dev_cs89x0.base_addr = io; dev_cs89x0.init = cs89x0_probe; //对struct net_devices初始化 request_region(dev_cs89x0.base_addr, NETCARD_IO_EXTENT, "cs8900a");
//申请使用io端口
if (register_netdev(&dev_cs89x0) != 0) //注册网卡驱动
}
2.设备检测和设置
static int __init cs89x0_probe1(struct net_device *dev, int ioaddr) { rev_type = readreg(dev, PRODUCT_ID_ADD); lp->chip_type = rev_type &~ REVISON_BITS; lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A'; if (lp->chip_type != CS8900) { printk(__FILE__ ": wrong device driver!\n"); ret = -ENODEV; goto after_kmalloc; } dev->dev_addr[0] = 0x00; dev->dev_addr[1] = 0x00; dev->dev_addr[2] = 0xc0; dev->dev_addr[3] = 0xff; dev->dev_addr[4] = 0xee; dev->dev_addr[5] = 0x08; set_mac_address(dev, dev->dev_addr); dev->irq = IRQ_LAN; printk(", IRQ %d", dev->irq); dev->open = net_open; dev->stop = net_close; dev->tx_timeout = net_timeout; dev->watchdog_timeo = 3 * HZ; dev->hard_start_xmit = net_send_packet; dev->get_stats = net_get_stats; dev->set_multicast_list = set_multicast_list; dev->set_mac_address = set_mac_address; ether_setup(dev); }
3.数据发送
static int net_send_packet(struct sk_buff *skb, struct net_device *dev) { netif_stop_queue(dev);//通知内核开始发送数据,停止发送队列等操作 writeword(dev, TX_CMD_PORT, lp->send_cmd);//发送命令到命令寄存器 writeword(dev, TX_LEN_PORT, skb->len);//数据长度到寄存器 if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) { spin_unlock_irq(&lp->lock); DPRINTK(1, "cs89x0: Tx buffer not free!\n"); return 1; } writeblock(dev, skb->data, skb->len);//数据写入PORT0寄存器中
return 0; }
4.中断static irqreturn_t net_interrupt(int irq, void *dev_id)
{ struct net_device *dev = dev_id; struct net_local *lp; int status; int handled = 0; lp = netdev_priv(dev); while ((status = ioread16(lp->virt_addr + ISQ_PORT))) { handled = 1; switch (status & ISQ_EVENT_MASK) { case ISQ_RECEIVER_EVENT: net_rx(dev);//turn to receive data
break; case ISQ_TRANSMITTER_EVENT: dev->stats.tx_packets++; netif_wake_queue(dev); if ((status & (TX_OK | TX_LOST_CRS | TX_SQE_ERROR | TX_LATE_COL | TX_16_COL)) != TX_OK) { if ((status & TX_OK) == 0) dev->stats.tx_errors++; if (status & TX_LOST_CRS) dev->stats.tx_carrier_errors++; if (status & TX_SQE_ERROR) dev->stats.tx_heartbeat_errors++; if (status & TX_LATE_COL) dev->stats.tx_window_errors++; if (status & TX_16_COL) dev->stats.tx_aborted_errors++; } break; case ISQ_BUFFER_EVENT: if (status & READY_FOR_TX) { netif_wake_queue(dev); } if (status & TX_UNDERRUN) { cs89_dbg(0, err, "%s: transmit underrun\n", dev->name); lp->send_underrun++; if (lp->send_underrun == 3) lp->send_cmd = TX_AFTER_381; else if (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL; netif_wake_queue(dev); /* Inform upper layers. */ } break; case ISQ_RX_MISS_EVENT: dev->stats.rx_missed_errors += (status >> 6); break; case ISQ_TX_COL_EVENT: dev->stats.collisions += (status >> 6); break; } } return IRQ_RETVAL(handled); }
5、接收数据
static void net_rx(struct net_device *dev) { struct net_local *lp = netdev_priv(dev); struct sk_buff *skb; int status, length; status = ioread16(lp->virt_addr + RX_FRAME_PORT); length = ioread16(lp->virt_addr + RX_FRAME_PORT); if ((status & RX_OK) == 0) { count_rx_errors(status, dev); return; } skb = netdev_alloc_skb(dev, length + 2); //分配sk_buff if (skb == NULL) { dev->stats.rx_dropped++; return; } skb_reserve(skb, 2); /* 目的mac地址6+原mac地址6+数据字段长度2 = 14 */ readwords(lp, RX_FRAME_PORT, skb_put(skb, length), length >> 1); if (length & 1) skb->data[length-1] = ioread16(lp->virt_addr + RX_FRAME_PORT); skb->protocol = eth_type_trans(skb, dev); netif_rx(skb);//交给设备无关层 dev->stats.rx_packets++; dev->stats.rx_bytes += length; }

浙公网安备 33010602011771号