1 / 26

Linux 操作系统分析与实践 第九讲:网络设备驱动程序

Linux 操作系统分析与实践 第九讲:网络设备驱动程序. 《 Linux 操作系统分析与实践 》 课程建设小组 北京大学 二零零八年春季 *致谢:感谢 Intel 对本课程项目的资助. 本讲主要内容. 网络接口驱动程序 网络接口例子 —snull. 9.1 网络接口驱动程序. 网络接口驱动程序和字符设备、块设备驱动程序都不同 本次课介绍一个不和真实硬件相关的“虚拟”网络接口. 设备注册. <linux/netdevice.h> struct net_device 这个结构中包括了网络设备接口需要的很多信息 必须动态分配,例如

arama
Download Presentation

Linux 操作系统分析与实践 第九讲:网络设备驱动程序

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Linux操作系统分析与实践第九讲:网络设备驱动程序 《Linux操作系统分析与实践》课程建设小组 北京大学 二零零八年春季 *致谢:感谢Intel对本课程项目的资助

  2. 本讲主要内容 • 网络接口驱动程序 • 网络接口例子—snull

  3. 9.1 网络接口驱动程序 • 网络接口驱动程序和字符设备、块设备驱动程序都不同 • 本次课介绍一个不和真实硬件相关的“虚拟”网络接口

  4. 设备注册 • <linux/netdevice.h> • struct net_device • 这个结构中包括了网络设备接口需要的很多信息 • 必须动态分配,例如 • struct net_device *snull_devs[2]; • struct net_device *alloc_netdev(int sizeof_priv, const char *name, void (*setup)(struct net_device *)); • struct net_device *alloc_netdev(int sizeof_priv, /*私有数据区长度 ,驱动程序设计者设计*/ const char *name,/*接口名 */ void (*setup)(struct net_device *) /* 网络接口初始化函数 */ );

  5. 例子 snull_devs[0] = alloc_netdev(sizeof(struct snull_priv), "sn%d", snull_init); snull_devs[1] = alloc_netdev(sizeof(struct snull_priv), "sn%d", snull_init); if (snull_devs[0] = = NULL || snull_devs[1] = = NULL) goto out;

  6. alloc_etherdev • 除了alloc_netdev,也有一些简化工作的函数,比如: • struct net_device *alloc_etherdev(int sizeof_priv); • 这个函数不用驱动程序提供接口初始化函数,网络接口名是 eth%d 这样的形式 • 在<linux/etherdevice.h>中定义

  7. register_netdev • for (i = 0; i < 2; i++) if ((result = register_netdev(snull_devs[i]))) printk("snull: error %i registering device \"%s\"\n", result, snull_devs[i]->name);

  8. 初始化设备 ether_setup(dev); /* 对dev的某些域先进行初始化*/ dev->open = snull_open; dev->stop = snull_release; dev->set_config = snull_config; dev->hard_start_xmit = snull_tx; dev->do_ioctl = snull_ioctl; dev->get_stats = snull_stats; dev->rebuild_header = snull_rebuild_header; dev->hard_header = snull_header; dev->tx_timeout = snull_tx_timeout; dev->watchdog_timeo = timeout; /* keep the default flags, just add NOARP */ dev->flags |= IFF_NOARP; dev->features |= NETIF_F_NO_CSUM; dev->hard_header_cache = NULL; /* Disable caching */ 网络设备的一系列操作和 状态标志

  9. 程序员定义的结构snull_priv struct snull_priv { struct net_device_stats stats; //统计信息 int status;//状态 struct snull_packet *ppool; struct snull_packet *rx_queue; /* List of incoming packets */ int rx_int_enabled; int tx_packetlen; u8 *tx_packetdata; struct sk_buff *skb; spinlock_t lock; }; 通过netdev_priv()来获得设备 的私有数据区

  10. 清理工作 void snull_cleanup(void) { int i; for (i = 0; i < 2; i++) { if (snull_devs[i]) { unregister_netdev(snull_devs[i]); snull_teardown_pool(snull_devs[i]); free_netdev(snull_devs[i]); } } return; }

  11. net_device 结构中重要数据介绍 • char name[IFNAMSIZ]; • 网络接口名,如果名字中包括 %d,那么 register_netdev 替换它为唯一名字,从0开始分配数字 • unsigned long state; • 驱动状态 • struct net_device *next; • 在全局接口链表中指向下一个接口结构 • int (*init)(struct net_device *dev); • 初始化函数。被register_netdev调用完成初始化。现在一般不使用这个函数,而是在调用register_netdev前就对结构进行初始化

  12. Cont. • unsigned long rmem_end; • unsigned long rmem_start; • unsigned long mem_end; • unsigned long mem_start; • Device memory information. These fields hold the beginning and ending addresses of the shared memory used by the device. Rmem 代表接收内存;mem_代表发送内存 • unsigned long base_addr; • The I/O base address of the network interface. • unsigned char irq; • The assigned interrupt number. • unsigned char if_port; • The port in use on multiport devices. • unsigned char dma; • The DMA channel allocated by the device.

  13. 比较重要的网络设备驱动方法 • int (*open)(struct net_device *dev); • Opens the interface. The interface is opened whenever ifconfig activates it. The open method should register any system resource it needs (I/O ports, IRQ, DMA, etc.), turn on the hardware, and perform any other setup your device requires. • int (*stop)(struct net_device *dev); • Stops the interface. The interface is stopped when it is brought down. This function should reverse operations performed at open time

  14. Cont. • int (*hard_start_xmit) (struct sk_buff *skb, struct net_device *dev); • Method that initiates the transmission of a packet. The full packet (protocol headers and all) is contained in a socket buffer (sk_buff) structure. Socket buffers are introduced later in this chapter. • int (*hard_header) (struct sk_buff *skb, struct net_device *dev, unsigned • short type, void *daddr, void *saddr, unsigned len); • Function (called before hard_start_xmit) that builds the hardware header from the source and destination hardware addresses that were previously retrieved; its job is to organize the information passed to it as arguments into an appropriate, device-specific hardware header. eth_header is the default function for Ethernet-like interfaces, and ether_setup assigns this field accordingly.

  15. Cont. • void (*tx_timeout)(struct net_device *dev); • Method called by the networking code when a packet transmission fails to complete within a reasonable period, on the assumption that an interrupt has been missed or the interface has locked up. It should handle the problem and resume packet transmission. • struct net_device_stats *(*get_stats)(struct net_device *dev); • Whenever an application needs to get statistics for the interface, this method is called. This happens, for example, when ifconfig or netstat -i is run. • int (*set_mac_address)(struct net_device *dev, void *addr); • int (*change_mtu)(struct net_device *dev, int new_mtu); • maximum transfer unit (MTU)

  16. 9.2 网络接口例子 snull • 打开设备 int snull_open(struct net_device *dev) { /* request_region( ), request_irq( ), .... (like fops->open) * Assign the hardware address of the board: use "\0SNULx", where * x is 0 or 1. The first byte is '\0' to avoid being a multicast * address (the first byte of multicast addrs is odd). */ memcpy(dev->dev_addr, "\0SNUL0", ETH_ALEN); if (dev = = snull_devs[1]) dev->dev_addr[ETH_ALEN-1]++; /* \0SNUL1 */ netif_start_queue(dev); return 0; } netif_start_queue()使能处理数据; netif_stop_queue()停止处理数据

  17. Cont. • 关闭设备 int snull_release(struct net_device *dev) { /* release ports, irq and such -- like fops->close */ netif_stop_queue(dev); /* can't transmit any more */ return 0; }

  18. 发送数据 int snull_tx(struct sk_buff *skb, struct net_device *dev) { int len; char *data, shortpkt[ETH_ZLEN]; struct snull_priv *priv = netdev_priv(dev); data = skb->data; len = skb->len; if (len < ETH_ZLEN) { memset(shortpkt, 0, ETH_ZLEN); memcpy(shortpkt, skb->data, skb->len); len = ETH_ZLEN; data = shortpkt; } 要发送的数据保存在 Skb->data 指向的位置 从skb->data拷贝数据到要保存 发送数据的缓冲区shortpkt

  19. Cont. dev->trans_start = jiffies; /* save the timestamp */ /* Remember the skb, so we can free it at interrupt time */ priv->skb = skb; /* actual deliver of data is device-specific, and not shown here */ snull_hw_tx(data, len, dev); return 0; /* Our simple device can not fail */ } 在snull_hw_tx中,实际上我们可以 编写程序实现具体硬件的发送数据 的控制,这里的snull_hw_tx并 没有对哪个具体的硬件进行操作。

  20. snull_hw_tx /* * Transmit a packet (low level interface) */ static void snull_hw_tx(char *buf, int len, struct net_device *dev) { /* * This function deals with hw details. This interface loops * back the packet to the other snull interface (if any). * In other words, this function implements the snull behaviour, * while all other procedures are rather device-independent */ struct iphdr *ih; struct net_device *dest; struct snull_priv *priv; u32 *saddr, *daddr; struct snull_packet *tx_buffer;

  21. /* I am paranoid. Ain't I? */ if (len < sizeof(struct ethhdr) + sizeof(struct iphdr)) { printk("snull: Hmm... packet too short (%i octets)\n", len); return; } /* * Ethhdr is 14 bytes, but the kernel arranges for iphdr * to be aligned (i.e., ethhdr is unaligned) */ ih = (struct iphdr *)(buf+sizeof(struct ethhdr)); saddr = &ih->saddr; daddr = &ih->daddr; ((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) */ ((u8 *)daddr)[2] ^= 1; ih->check = 0; /* and rebuild the checksum (ip needs it) */ ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl);

  22. if (dev == snull_devs[0]) PDEBUGG("%08x:%05i --> %08x:%05i\n", ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source), ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest)); else PDEBUGG("%08x:%05i <-- %08x:%05i\n", ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest), ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source)); /* * Ok, now the packet is ready for transmission: first simulate a * receive interrupt on the twin device, then a * transmission-done on the transmitting device */ dest = snull_devs[dev == snull_devs[0] ? 1 : 0]; priv = netdev_priv(dest); tx_buffer = snull_get_tx_buffer(dev); tx_buffer->datalen = len; memcpy(tx_buffer->data, buf, len); snull_enqueue_buf(dest, tx_buffer);

  23. Cont. if (priv->rx_int_enabled) { priv->status |= SNULL_RX_INTR; snull_interrupt(0, dest, NULL); } priv = netdev_priv(dev); priv->tx_packetlen = len; priv->tx_packetdata = buf; priv->status |= SNULL_TX_INTR; if (lockup && ((priv->stats.tx_packets + 1) % lockup) == 0) { /* Simulate a dropped transmit interrupt */ netif_stop_queue(dev); PDEBUG("Simulate lockup at %ld, txp %ld\n", jiffies, (unsigned long) priv->stats.tx_packets); } else snull_interrupt(0, dev, NULL); }

  24. 模拟真实中断的中断处理程序 static void snull_napi_interrupt(int irq, void *dev_id, struct pt_regs *regs) { int statusword; struct snull_priv *priv; /* * As usual, check the "device" pointer for shared handlers. * Then assign "struct device *dev" */ struct net_device *dev = (struct net_device *)dev_id; /* ... and check with hw if it's really ours */ /* paranoid */ if (!dev) return;

  25. Cont. /* Lock the device */ priv = netdev_priv(dev); spin_lock(&priv->lock); /* retrieve statusword: real netdevices use I/O instructions */ statusword = priv->status; priv->status = 0; if (statusword & SNULL_RX_INTR) { snull_rx_ints(dev, 0); /* Disable further interrupts */ netif_rx_schedule(dev); } if (statusword & SNULL_TX_INTR) { /* a transmission is over: free the skb */ priv->stats.tx_packets++; priv->stats.tx_bytes += priv->tx_packetlen; dev_kfree_skb(priv->skb); } /* Unlock the device and we are done */ spin_unlock(&priv->lock); return; }

  26. Q&A 本讲结束 !

More Related