网卡驱动

裸机 时序

1. 读寄存器:

1. *(unsigned int *)0x5000000 = reg; //从哪个寄存器读
 //0x5000000为bank1,访问bank1时他会自动产生nGCSx信号和WE信号,
 // MCU的地址总线上会出现0x50000000,数据总线 上会出现reg.
 //因为DM9000的cmd接的是地址总线的ADDR[2],故此时cmd为低电平,表示16位端口上出现的
是地址,又因为WE有效故是写,这样DM9000就会锁存SD[160]上的电平作为接下来即将操作的寄存器地址。
2.  int a = *(unsigned int *)0x5000004;//准备接受数据
//0x5000004为bank1,访问bank1时他会自动产生nGCSx信号和RE信号,
// MCU的地址总线上会出现0x50000004,数据总线 上会出现DM9000送出来的数据.
//因为DM9000的cmd接的是地址总线的ADDR[2],故此时cmd为高电平,表示16位端口上出现的
是数据,有因为RE有效故是读,这样DM9000就会把数据送到SD[160]上。


封装函数为     int DM9000_read_reg(u32 reg);

2.写寄存器:

1. *(unsigned int *)0x5000000 = reg;
2.  *(unsigned int *)0x5000004 = data;


封装函数为 void DM9000_write_reg(u32 reg, u32 data);

3.读SRAM:

从DM9000的SRAM中某个地址处读取数据
//1.告诉DM9000从SRAM中哪个位置读
DM9000_write_reg(MDRAH, ADDR_H);
DM9000_write_reg(MDRAL, ADDR_L);
//2.读数据
int a = DM9000_read_reg(MRCMD);
//3.连续读
int a = DM9000_read_reg(MRCMD);//SRAM中的地址会自动增1

4.写SRAM:

把一个数写到DM9000的SRAM(一般总共16K低3K用于发送,高13K用于接收)
//1.告诉DM9000往SRAM中哪个位置写
DM9000_write_reg(MDRAH, ADDR_H);
DM9000_write_reg(MDRAL, ADDR_L);
//2.要写的数值
DM9000_write_reg(MWCMD, data);
//3.连续写
DM9000_write_reg(MWCMD, data1); //SRAM地址会自动增1

5.将SRAM中的数发向RJ45:

这个过程是DM9000内部的一次DMA传输
//1.在SRAM的0首地址处填充要发送的数据包,即一系列的写SRAM的操作
//2.在发送控制寄存器TCR中使能时钟和CRC校验
//3.把要发送的长度放入发送长度寄存器中
DM9000_write_reg(TXPLL_L, len_L);
DM9000_write_reg(TXPLL_H, len_H);
//4.置位发送控制寄存器第0位,启动发送,发送完成后硬件自动将该位清0
DM9000_write_reg(TXPLL_H, len_H);

6.从RJ45接收数据到DM9000的SRAM中:

//1.DM9000会自动从网络接收数据包,存放到SRAM的高13K处
//2.接受完成后会自动添加4字节的包头,01h、状态、包长高字节、包长低字节
//3.DM9000自动置中断标志位并由中断引脚触发中断叫醒4412
//4.4412cpu响应中断,从DM9000的SRAM中读取包头并分析后,按包头指示操作

内核提供的接口函数:

1.数据结构:

<include/linux/netdevice.h>

struct net_device {
    char            name[IFNAMSIZ];   //物理接口名(网卡名)
    struct hlist_node   name_hlist;
    char            *ifalias;
    /*物理信息:IO地址、中断号*/
    unsigned long       mem_end;     //缓存结束
    unsigned long       mem_start;   //缓存起始
    unsigned long       base_addr;   //基地址
    int         irq;                 //中断
    netdev_features_t   features;    //可变特性,DMA,校验等一系列附加特性
    netdev_features_t   hw_features; //用户不能改变的私有特性
    unsigned int        flags;       //可变标准
    unsigned int        priv_flags;  //用户不能改变的标准
    const struct net_device_ops *netdev_ops;   //操作方法集
    const struct ethtool_ops *ethtool_ops;     //eth工具集操作方法集
    const struct header_ops *header_ops;       //协议头操作方法集
    unsigned char       if_port;               //接口类型
    unsigned int        mtu;                   //最大包长      
    unsigned short      type;                  //网络包类型
    unsigned short      hard_header_len;       //硬件协议头长度
    unsigned char   perm_addr[MAX_ADDR_LEN];   //MAC地址
    unsigned char       addr_len;              //MAC地址长度
    unsigned short          dev_id;            //指向mac地址
    struct in_device __rcu  *ip_ptr;           //ip地址
    ...
};
<include/linux/netdevcice.h>

struct net_device_ops {
    int     (*ndo_init)(struct net_device *dev);  //构造初始化对象
    void    (*ndo_uninit)(struct net_device *dev);//析构函数,回收清理
    int     (*ndo_open)(struct net_device *dev);  //打开设备
    int     (*ndo_stop)(struct net_device *dev);  //停止
    netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb,
                          struct net_device *dev);//发送
    int     (*ndo_set_mac_address)(struct net_device *dev,
                               void *addr);      //设置mac地址
    int  (*ndo_validate_addr)(struct net_device *dev);//检查MAC是否有效
    int     (*ndo_do_ioctl)(struct net_device *dev,
                            struct ifreq *ifr, int cmd);//ioctl
    int (*ndo_set_config)(struct net_device *dev,
                          struct ifmap *map);//修改网卡io地址、中断等网卡物理信息
    int         (*ndo_change_mtu)(struct net_device *dev,
                          int new_mtu);   //设置最大包长
    void    (*ndo_tx_timeout) (struct net_device *dev);  //超时重发
    ...

2.通用的函数:

步骤性的:

/*
* 功能:创建一个以太网设备对象
* 输入参数:私有数据大小
* 返回值:成功:设备句柄     失败:NULL
*/
 struct net_device *alloc_etherdev(sizeof_priv)
/*
* 功能:创建一个网络设备对象
* 输入参数:sizeof_priv:私有数据大小
*          name:网卡名字(名称+%d)
*          name_assign_type:NET_NAME_UNKNOWN
*          setup:初始化函数指针(回调函数)
* 返回值:成功:设备句柄     失败:NULL
*/
struct net_device *alloc_netdev(sizeof_priv, name,      name_assign_type, setu
/*
* 功能:释放一个网络设备对象
* 输入参数:设备句柄
* 返回值:none
*/
void free_net_devive(struct net_device *dev);
/*
* 功能:以太网设备初始化
* 输入参数:设备句柄
* 返回值:none
*/
void ether_setup(struct net_device *dev);
/*
* 功能:注册网络设备
* 输入参数:设备句柄
* 返回值:成功:0  失败:负数
*/
int register_netdev(struct net_device *dev);
/*
* 功能:注销网络设备
* 输入参数:设备句柄
* 返回值:成功:0  失败:负数
*/
int unregister_netdevice(struct net_device *dev);

设置参数的:

/*
* 功能:提取私有数据
* 输入参数:设备句柄
* 返回值:成功:私有数据首地址  失败:NULL
*/
void *netdev_priv(struct net_device *dev)
/*
* 功能:设置mac地址
* 输入参数:设备句柄
*        p:MAC地址所在首地址
* 返回值:成功:0  失败:负数
*/
int eth_mac_addr(struct net_device *dev, void *p)
/*
* 功能:随机生成mac地址,并将生成的mac地址填充到传入的dev结构体的对应域
* 输入输出参数:设备句柄
* 返回值:none
*/
void eth_hw_addr_random(struct net_device *dev)
/*
* 功能:随机生成mac地址
* 输出参数:addr:将得到的结果放在addr中
* 返回值:none
*/
void eth_random_addr(u8 *addr);
/*
* 功能:检查mac地址是否有效
* 输入参数:设备句柄
* 返回值:有效:1  无效:0
*/
int eth_validate_addr(struct net_device *dev);
/*
* 功能:修改MTU值
* 输入参数:dev:         设备句柄
*        new_mtu:       新的MTU值(最大包长)
* 返回值:成功:0  失败:负数
*/
int eth_change_mtu(struct net_device *dev, int new_mtu);

发送相关的:

类比块设备的请求队列记忆,每一个发送请求都是经过调度算法处理后放在发送队列
中,驱动层的发送回调函数只要从队列中取出任务处理每个发送请求就行。故要响应
用户层的发送请求就必须开启请求队列。
/*
* 功能:开启发送队列
* 输入参数:dev:         设备句柄
* 返回值:none
*/
void netif_start_queue(struct net_device *dev)
/*
* 功能:关闭发送队列
* 输入参数:dev:         设备句柄
* 返回值:none
*/
void netif_stop_queue(struct net_device *dev)
/*
* 功能:打开载波调制
* 输入参数:dev:         设备句柄
* 返回值:none
*/
void netif_carrier_on(struct net_device *dev)
/*
* 功能:关闭载波调制
* 输入参数:dev:         设备句柄
* 返回值:none
*/
void netif_carrier_off(struct net_device *dev)

套接字缓冲区相关的:

/*
* 功能:开辟一块套接字缓冲区
* 输入参数:unsigned int size:大小
*         gfp_t priority:   权限
* 返回值:缓冲区首地址
*/
struct sk_buff *alloc_skb(unsigned int size, gfp_t priority)
/*
* 功能:开辟一块套接字缓冲区
* 输入参数:  struct net_device dev: 网络设备句柄
*           unsigned int size:     大小
* 返回值:缓冲区首地址
*/
struct sk_buff *netdev_alloc_skb(struct net_device *dev, 
                                 unsigned int length)
/*
* 功能:释放一块套接字缓冲区
* 输入参数:  struct sk_buff *skb:缓冲区首地址 
* 返回值:none
*/
void kfree_skb(struct sk_buff *skb);
/*
* 功能:释放一块套接字缓冲区
* 输入参数:  struct sk_buff *skb:缓冲区首地址 
* 返回值:none
*/
void dev_kfree_skb_any(struct sk_buff *skb);
/*
* 功能:tail增大,有效数据区变大
* 输入参数:  struct sk_buff *skb: 缓冲区首地址 
*           unsigned int len:    要增大多少
* 返回值:返回tail指针
*/
unsigned char *skb_put(struct sk_buff *skb, unsigned int len);
/*
* 功能:data下移,有效数据区变小
* 输入参数:  struct sk_buff *skb: 缓冲区首地址 
*           unsigned int len:    要下移多少
* 返回值:返回data指针
*/
unsigned char *skb_push(struct sk_buff *skb, unsigned int len);
/*
* 功能:data下移,tail增大,有效数据区不变
* 输入参数:  struct sk_buff *skb: 缓冲区首地址 
*           unsigned int len:    要下移多少
* 返回值:none
*/
void skb_reserve(struct sk_buff *skb, int len)
/*
* 功能:把sk_buff提交给协议层
* 输入参数:  struct sk_buff *skb: 缓冲区首地址 
* 返回值:成功:0(NET_RX_SUCCESS)  失败:1(NET_RX_DROP)
*/
int netif_rx(struct sk_buff *skb)
/*
* 功能:得到协议编号
* 输入参数:  struct sk_buff *skb:     缓冲区首地址 
*           struct net_device *dev:  网络设备句柄
* 返回值:成功:得到的编号  失败:负数
*/
__be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)

例程:

1.c文件

2.h文件

源码追踪:

见思维导图

源码下载: https://git.coding.net/xxgui1992/LinuxDriver-net.git

原文地址:https://www.cnblogs.com/xxg1992/p/6636372.html