网卡驱动程序

  如果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 的内部寄存器,所以还
需要一个控制接口,叫做 MIDOMDIO 很类似 IIC,也是两根线,一根数据线叫做 MDIO,一根时钟线叫做 MDC

 

 

 接口

(1)MII 接口

   MII 全称是 Media Independent Interface,直译过来就是介质独立接口,它是 IEEE-802.3 定义的以太网标准接口, MII 接口用于以太网 MAC 连接 PHY 芯片,连接示意图如图 69.1.2.1 所示

 

 

MII 接口一共有 16 根信号线,含义如下:
TX_CLK: 发送时钟,如果网速为 100M 的话时钟频率为 25MHz10M 网速的话时钟频率
2.5MHz,此时钟由 PHY 产生并发送给 MAC
TX_EN: 发送使能信号。
TX_ER: 发送错误信号,高电平有效,表示 TX_ER 有效期内传输的数据无效。 10Mpbs
速下 TX_ER 不起作用。
TXD[3:0]:发送数据信号线,一共 4 根。
RXD[3:0]: 接收数据信号线,一共 4 根。
RX_CLK: 接收时钟信号,如果网速为 100M 的话时钟频率为 25MHz10M 网速的话时钟
频率为 2.5MHzRX_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 实例。
pollNAPI 所使用的轮询函数,非常重要,一般在此轮询函数中完成网络数据接收的工作。
weightNAPI 默认权重(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;
}

 





posted @ 2020-11-20 11:33  11YS  阅读(1050)  评论(0)    收藏  举报