网卡驱动程序框架
网卡驱动程序“收发功能”:
只要把上层的数据发给网卡,从网卡来的数据构造成包给上层即可。网卡只需要
“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