自学内容网 自学内容网

虚拟网卡驱动和DM9000C移植

网卡驱动程序框架

网卡驱动程序“收发功能”:

只要把上层的数据发给网卡,从网卡来的数据构造成包给上层即可。网卡只需要

“socket”编程,不需要打开某设备。

驱动程序都是以面向对象的思想写的,都有相关的结构体。

编程步骤

1,分配某结构体:alloc_netdev net_device

2,设置结构体。

①,提供一个发包函数:hard_start_xmit()

②,提供收包的功能 : net_interrupt(int irq, void *dev_id)-->netif_rx(skb);

收到数据后,网卡里面一般都有中断程序。在中断程序中有一个上报数据的函数。

3,注册结构体:register_netdev(dev) 真实驱动中使用的是此注册函数。

4,硬件相关操作。

看内核中的“cs89x.c”这个真实的网卡驱动程序:

int __init init_module(void)

    -->分配一个 net_device 结构体

    struct net_device *dev = alloc_etherdev(sizeof(struct net_local));

        --->alloc_netdev(sizeof_priv, "eth%d", ether_setup); 分配时用了eth%d这样的名字。

    -->设置 net_device 结构 :

    MAC 地址,硬件相关操作

    dev->dev_addr[0] = 0x08;

    dev->dev_addr[1] = 0x89;

    dev->dev_addr[2] = 0x89;

    dev->dev_addr[3] = 0x89;

    dev->dev_addr[4] = 0x89;

    dev->dev_addr[5] = 0x89; //以上为MAC地址。

    --> 硬件相关设置

    -->ret = cs89x0_probe1(dev, io, 1);

    -->net_device结构中有open,read等函数。

    dev->open = net_open;

    dev->stop = net_close;

    dev->tx_timeout = net_timeout;

    dev->watchdog_timeo = 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;

    -->retval = register_netdev(dev);

接收到包后是上报了一个“sk_buff”缓冲(netif_rx(skb))

sk_buff 结构 是纽带,运用“hard_start_xmit()”和“netif_rx()”:

网卡在中断程序中收到数据后,从芯片里把数据读出来构造一个“skb_buff”结构数据,

调用“netif_rx()”上报数据给应用层。

接收到数据要做的事情:

irqreturn_t net_interrupt(int irq, void *dev_id)

    -->net_rx(dev);

    -->从硬件芯片里读出来

    status = readword(ioaddr, RX_FRAME_PORT);

    length = readword(ioaddr, RX_FRAME_PORT);

    -->skb = dev_alloc_skb(length + 2); 分配一个skb缓冲。

    -->netif_rx(skb);

    -->netif_wake_queue(dev); 发送完数据后就唤醒队列

应用层构造好一个包后,放到“skb_buff”结构交给网卡驱动,调用

“hard_start_xmit()”来发送。

发包函数:

int net_send_packet(struct sk_buff *skb, struct net_device *dev)

    -->netif_stop_queue(dev); 先停止队列

    -->将“skb_buff”中数据写到网卡芯片:

    writeword(dev->base_addr, TX_CMD_PORT, lp->send_cmd);

    writeword(dev->base_addr, TX_LEN_PORT, skb->len);

    writewords(dev->base_addr, TX_FRAME_PORT,skb->data,(skb->len+1) >>1);

    -->dev_kfree_skb (skb); 然后释放skb_buff

16th_virt_net难懂代码分析

测试ifconfig vnet0 3.3.3.3

ping 3.3.3.4

模拟真实网卡ping功能

static void emulator_rx_packet(struct sk_buff *skb, struct net_device *dev)

{

    /* 参考LDD3 */

    unsigned char *type;

    struct iphdr *ih;

    __be32 *saddr, *daddr, tmp;

    unsigned char    tmp_dev_addr[ETH_ALEN];

    struct ethhdr *ethhdr;

    

    struct sk_buff *rx_skb;

        

    // 从硬件读出/保存数据

    /* 对调"源/目的"的mac地址 */

    ethhdr = (struct ethhdr *)skb->data;

    memcpy(tmp_dev_addr, ethhdr->h_dest, ETH_ALEN);

    memcpy(ethhdr->h_dest, ethhdr->h_source, ETH_ALEN);

    memcpy(ethhdr->h_source, tmp_dev_addr, ETH_ALEN);

    /* 对调"源/目的"的ip地址 */    

    ih = (struct iphdr *)(skb->data + sizeof(struct ethhdr));

    saddr = &ih->saddr;

    daddr = &ih->daddr;

    tmp = *saddr;

    *saddr = *daddr;

    *daddr = tmp;

    

    //((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) */

    //((u8 *)daddr)[2] ^= 1;

    type = skb->data + sizeof(struct ethhdr) + sizeof(struct iphdr);

    //printk("tx package type = %02x\n", *type);

    // 修改类型, 原来0x8表示ping

    *type = 0; /* 0表示reply */

    

    ih->check = 0;           /* and rebuild the checksum (ip needs it) */

    ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl);

    

    // 构造一个sk_buff

    rx_skb = dev_alloc_skb(skb->len + 2);

    skb_reserve(rx_skb, 2); /* align IP on 16B boundary */    

    memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len);

    /* Write metadata, and then pass to the receive level */

    rx_skb->dev = dev;

    rx_skb->protocol = eth_type_trans(rx_skb, dev);

    rx_skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */

    dev->stats.rx_packets++;

    dev->stats.rx_bytes += skb->len;

    // 提交sk_buff

    netif_rx(rx_skb);

}

DM9000移植修改

修改点:

1.网卡基地址,位宽,中断号

2.设置网卡访问时序

jz2440在ubuntu18.0 挂载

mount -t nfs -o nolock,vers=3 10.10.60.95:/home/book/jz2440/nfs_rootfs/fs_test_mini_new /mnt

使用在ubuntu18.0 nfs启动

set  bootargs noinitrd root=/dev/nfs nfsroot=10.10.60.95:/home/book/jz2440/nfs_rootfs/fs_test_mini_new,v3,tcp ip=10.10.60.40:10.10.60.95:10.10.60.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0,115200


原文地址:https://blog.csdn.net/zhg598242449/article/details/143925128

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!