1. Linux网络体系结构由以下5部分组成
① 系统调用接口: 位于Linux网络子系统的顶部,为应用程序提供访问内核网络子系统的方法,主要指socket系统调用。
② 协议无关接口: 实现一组基于socket的通用函数来访问不同的协议。(Linux中的socket使用sock结构来描述(定义于include/net/sock.h),该结构包含特定socket所需要的所有状态信息,还包含socket所使用的特定协议和在socket上可以执行的一些操作。)
③ 网络协议: 用于实现具体的网络协议,如TCP、UDP等
④ 设备无关接口: 将协议与各种网络设备驱动连接在一起
⑤ 设备驱动:负责管理物理网络设备
注:与字符设备与块设备不同,网络接口设备在Linux的/dev目录下不存在与之对应的设备文件,因此无法向字符设备或块设备那样通过访问设备文件来操作。Linux通过一些系统提供的工具来访问和设置网络设备,如ifconfig、mii-tool、ethtool等。
2. 每个网络设备都由一个net_device结构来描述,该结构位于include/linux/netdevice.h中定义
struct net_device { char name[IFNAMSIZ];/* unsigned long base_addr; /* device I/O address */ unsigned int irq; /* device IRQ number */
unsigned long state;
unsigned mtu; /* interface MTU value */
unsigned char dev_addr[MAX_ADDR_LEN]; /* hw address, (before bcast
/* Management operations */ const struct net_device_ops *netdev_ops; const struct ethtool_ops *ethtool_ops; /* Hardware header description */ const struct header_ops *header_ops;
... }
① name: 设备名称
② state: 设备状态
③ base_addr:网络接口的I/O基地址,由驱动在设备探测时赋值
④ irq:中断号
⑤ mtu:网卡设备可以处理的最大传输单元
⑥ dev_addr:设备的6字节MAC地址
⑦ netdev_ops:网络接口设备的操作接口函数集
3. 分配及注册网络设备
① 分配一个net_device结构可以使用宏:alloc_netdev
#define alloc_netdev(sizeof_priv, name, setup) \ alloc_netdev_mq(sizeof_priv, name, setup, 1)
② 对于不同类型的网络设备,内核提供了更直接的分配函数。如以太网网卡分配函数:alloc_etherdev
#define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1)
(1)alloc_etherdev_mq()函数只是对alloc_netdev_mq()函数的简单封装
struct net_device *alloc_etherdev_mq(int sizeof_priv, unsigned int queue_count) { return alloc_netdev_mq(sizeof_priv, "eth%d", ether_setup, queue_count); }
(2)ether_setup()是以太网设备专属初始化函数
void ether_setup(struct net_device *dev) { dev->header_ops = ð_header_ops; #ifdef CONFIG_COMPAT_NET_DEV_OPS dev->change_mtu = eth_change_mtu; dev->set_mac_address = eth_mac_addr; dev->validate_addr = eth_validate_addr; #endif dev->type = ARPHRD_ETHER; dev->hard_header_len = ETH_HLEN; dev->mtu = ETH_DATA_LEN; dev->addr_len = ETH_ALEN; dev->tx_queue_len = 1000; /* Ethernet wants good queues */ dev->flags = IFF_BROADCAST|IFF_MULTICAST; memset(dev->broadcast, 0xFF, ETH_ALEN); }
③ 注册网络设备:register_netdev
4. sk_buff称为“套接字缓冲区”,用于在网络子系统各层之间传递数据。
struct sk_buff { /* These two members must be first. */ struct sk_buff *next; struct sk_buff *prev; struct sock *sk; struct net_device *dev; __be16 protocol; void (*destructor)(struct sk_buff *skb); sk_buff_data_t transport_header; sk_buff_data_t network_header; sk_buff_data_t mac_header; /* These elements must be at the end, see alloc_skb() for details. */ sk_buff_data_t tail; sk_buff_data_t end; unsigned char *head, *data; unsigned int truesize; atomic_t users; };
① sk_buff定义了4个指向数据包缓冲区不同位置的指针head、data、tail、end。
(1) head与end分别指向数据包缓冲区的起始地址和结束地址,它们是在sk_buff和相关数据块分配后就固定下来
(2) data与tail分别指向当前协议层有效数据的起始地址和结束地址,因此随着sk_buff表示的数据包在不同协议层次间的传递,data与tail指针也会做相应的移动。
② 网卡接收到数据包,或者用户空间请求发送网络数据,skb都会被创建。内核中分配一个sk_buff使用函数:alloc_skb()
static inline struct sk_buff *alloc_skb(unsigned int size, gfp_t priority)
③ 在网卡驱动中,通常使用dev_alloc_skb()函数来分配一个skb。dev_alloc_skb()是对__dev_alloc_skb() 的简单封装,而__dev_alloc_skb()调用alloc_skb()以GFP_ATOMIC方式从内存分配skb,因此它可以用在中断上下文中。
struct sk_buff *dev_alloc_skb(unsigned int length) { return __dev_alloc_skb(length, GFP_ATOMIC); } static inline struct sk_buff *__dev_alloc_skb(unsigned int length, gfp_t gfp_mask) { struct sk_buff *skb = alloc_skb(length + NET_SKB_PAD, gfp_mask); if (likely(skb)) skb_reserve(skb, NET_SKB_PAD); return skb; }
注:GFP_ATOMIC,用来从中断处理和进程上下文之外的其他代码中分配内存,从不睡眠。
④ sk_buff的4个指针的基本操作函数
(1)skb_headroom: 获取sk_buff结构成员指针head到data之间空间的长度
static inline unsigned int skb_headroom(const struct sk_buff *skb) { return skb->data - skb->head; }
(2)skb_tailroom: 获取sk_buff结构成员指针tail到end之间空间的长度
static inline int skb_tailroom(const struct sk_buff *skb) { return skb_is_nonlinear(skb) ? 0 : skb->end - skb->tail; }
(3)skb_reserve: 将data和tail指针同时下移
static inline void skb_reserve(struct sk_buff *skb, int len) { skb->data += len; skb->tail += len; }
(4)skb_push: 将data指针上移,同时增加sk_buff中的len
unsigned char *skb_push(struct sk_buff *skb, unsigned int len) { skb->data -= len; skb->len += len; if (unlikely(skb->data<skb->head)) skb_under_panic(skb, len, __builtin_return_address(0)); return skb->data; }
(5)skb_pull: 将data指针下移,同时减少sk_buff中的len
unsigned char *skb_pull(struct sk_buff *skb, unsigned int len) { return unlikely(len > skb->len) ? NULL : __skb_pull(skb, len); } static inline unsigned char *__skb_pull(struct sk_buff *skb, unsigned int len) { skb->len -= len; BUG_ON(skb->len < skb->data_len); return skb->data += len; }
(6)skb_put:将tail指针下移len长度,并增加sk_buff中len的值,返回改变前的tail值
unsigned char *skb_put(struct sk_buff *skb, unsigned int len) { unsigned char *tmp = skb_tail_pointer(skb); SKB_LINEAR_ASSERT(skb); skb->tail += len; skb->len += len; if (unlikely(skb->tail > skb->end)) skb_over_panic(skb, len, __builtin_return_address(0)); return tmp; }
(7)skb_trim:重设data到tail之间有效数据长度为len
void skb_trim(struct sk_buff *skb, unsigned int len) { if (skb->len > len) __skb_trim(skb, len); } static inline void __skb_trim(struct sk_buff *skb, unsigned int len) { if (unlikely(skb->data_len)) { WARN_ON(1); return; } skb->len = len; skb_set_tail_pointer(skb, len); }
5. DM9000网卡驱动分析
① Linux2.6.29内核中已经有完整的DM9000网卡驱动,位于源文件drivers/net/dm9000.c。驱动将DM9000的IO内存资源和IRQ资源以平台设备资源的方式进行管理,并在dm9000.c中实现并注册了平台驱动,只要再向内核中注册板级相关的DM9000平台设备,网卡就能正常工作了。DM9000平台驱动定义如下
static struct platform_driver dm9000_driver = { .driver = { .name = "dm9000", .owner = THIS_MODULE, }, .probe = dm9000_probe, .remove = __devexit_p(dm9000_drv_remove), .suspend = dm9000_drv_suspend, .resume = dm9000_drv_resume, };
注:dm9000_probe是分析的重点。
② board_info结构:board_info是对net_device结构的扩展,以描述一个具体的网卡设备,也就是驱动中的DM9000。
/* Structure/enum declaration ------------------------------- */ typedef struct board_info { void __iomem *io_addr; /* Register I/O base address */ void __iomem *io_data; /* Data I/O address */ u16 irq; /* IRQ */ u16 tx_pkt_cnt; u16 queue_pkt_len; u16 queue_start_addr; u16 dbug_cnt; u8 io_mode; /* 0:word, 2:byte */ u8 phy_addr; u8 imr_all; unsigned int flags; unsigned int in_suspend :1; int debug_level; enum dm9000_type type; void (*inblk)(void __iomem *port, void *data, int length); void (*outblk)(void __iomem *port, void *data, int length); void (*dumpblk)(void __iomem *port, int length); struct device *dev; /* parent device */ struct resource *addr_res; /* resources found */ struct resource *data_res; struct resource *addr_req; /* resources requested */ struct resource *data_req; struct resource *irq_res; struct mutex addr_lock; /* phy and eeprom access lock */ struct delayed_work phy_poll; struct net_device *ndev; spinlock_t lock; struct mii_if_info mii; u32 msg_enable; } board_info_t;
注:net_device描述所有网卡的共性,按照面向对象的思想分析,board_info由net_device派生而来。驱动使用board_info的变量作为net_device对象的私有数据(紧跟着你net_device对象后存放,按32字节对齐)
③ dm9000_probe()函数
(1)在注册平台驱动时,内核遍历平台总线上的所有平台设备,通过名称匹配,并在找到匹配的设备后,调用平台驱动中的probe()方法。平台驱动通常利用probe()方法从匹配上的平台设备中获取平台资源,并根据这些资源申请和映射IO内存、获取并注册IRQ中断。DM9000_probe()除了做以上内容外,还最终调用register_netdev()注册网卡设备。
(2)dm9000_probe()函数分析1:获取设备私有数据(board_info结构),并为私有数据中的重要成员赋值。
static int __devinit dm9000_probe(struct platform_device *pdev) { struct dm9000_plat_data *pdata = pdev->dev.platform_data; struct board_info *db; /* Point a board information structure */ struct net_device *ndev;
...
/* Init network device */ ndev = alloc_etherdev(sizeof(struct board_info)); SET_NETDEV_DEV(ndev, &pdev->dev);/* setup board info structure */ db = netdev_priv(ndev); memset(db, 0, sizeof(*db)); db->dev = &pdev->dev; db->ndev = ndev; spin_lock_init(&db->lock); mutex_init(&db->addr_lock); INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);
对于代码INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work),安装延迟调度工作dm9000_poll_work,而dm9000_poll_work的作用是动态轮询检查DM9000的连接状态(由于DM9000E系列芯片不支持连接状态改变中断,故用此方法来检查连接状态)
(3)dm9000_probe()函数分析2:获取并保存平台资源和根据这些资源信息申请IO内存和中断
/* 获取平台资源 */
db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
/* 申请IO内存并映射--DM9000的INDEX端口 */ iosize = res_size(db->addr_res); db->addr_req = request_mem_region(db->addr_res->start, iosize, pdev->name); db->io_addr = ioremap(db->addr_res->start, iosize);
/* 申请IO内存并映射--DM9000的DATA端口 */ iosize = res_size(db->data_res); db->data_req = request_mem_region(db->data_res->start, iosize, pdev->name);
/* fill in parameters for net-dev structure */ db->io_data = ioremap(db->data_res->start, iosize); ndev->base_addr = (unsigned long)db->io_addr; ndev->irq = db->irq_res->start;
(4)dm9000_probe()函数分析3: 根据在平台设备中指定的私有数据,确定board_info结构中关于DM9000块读写的具体函数
/* ensure at least we have a default set of IO routines */ dm9000_set_io(db, iosize); /* check to see if anything is being over-ridden */ if (pdata != NULL) { /* check to see if the driver wants to over-ride the * default IO width */ if (pdata->flags & DM9000_PLATF_8BITONLY) dm9000_set_io(db, 1); if (pdata->flags & DM9000_PLATF_16BITONLY) dm9000_set_io(db, 2); if (pdata->flags & DM9000_PLATF_32BITONLY) dm9000_set_io(db, 4); /* check to see if there are any IO routine * over-rides */ if (pdata->inblk != NULL) db->inblk = pdata->inblk; if (pdata->outblk != NULL) db->outblk = pdata->outblk; if (pdata->dumpblk != NULL) db->dumpblk = pdata->dumpblk; db->flags = pdata->flags; }
(5)dm9000_probe()函数分析4:确定驱动找到的芯片是DM9000,并且确定芯片的具体型号。
dm9000_reset(db); /* try multiple times, DM9000 sometimes gets the read wrong */ for (i = 0; i < 8; i++) { id_val = ior(db, DM9000_VIDL); id_val |= (u32)ior(db, DM9000_VIDH) << 8; id_val |= (u32)ior(db, DM9000_PIDL) << 16; id_val |= (u32)ior(db, DM9000_PIDH) << 24; if (id_val == DM9000_ID) break; dev_err(db->dev, "read wrong id 0x%08x\n", id_val); } if (id_val != DM9000_ID) { dev_err(db->dev, "wrong id: 0x%08x\n", id_val); ret = -ENODEV; goto out; } /* Identify what type of DM9000 we are working on */ id_val = ior(db, DM9000_CHIPR); dev_dbg(db->dev, "dm9000 revision 0x%02x\n", id_val); switch (id_val) { case CHIPR_DM9000A: db->type = TYPE_DM9000A; break; case CHIPR_DM9000B: db->type = TYPE_DM9000B; break; default: dev_dbg(db->dev, "ID %02x => defaulting to DM9000E\n", id_val); db->type = TYPE_DM9000E; }
(6)dm9000_probe()函数分析5:进一步填充ndev和db这两个结构体变量的成员,注册网络设备。
/* from this point we assume that we have found a DM9000 */ /* driver system function */ ether_setup(ndev);
/* 网卡操作方法填充 */ ndev->open = &dm9000_open; ndev->hard_start_xmit = &dm9000_start_xmit; ndev->tx_timeout = &dm9000_timeout; ndev->watchdog_timeo = msecs_to_jiffies(watchdog); ndev->stop = &dm9000_stop; ndev->set_multicast_list = &dm9000_hash_table; ndev->ethtool_ops = &dm9000_ethtool_ops; ndev->do_ioctl = &dm9000_ioctl; #ifdef CONFIG_NET_POLL_CONTROLLER ndev->poll_controller = &dm9000_poll_controller; #endif /* 调试信息只对连接事件开放 */ db->msg_enable = NETIF_MSG_LINK;
/* 指定网卡MII的属性和方法 */ db->mii.phy_id_mask = 0x1f; db->mii.reg_num_mask = 0x1f; db->mii.force_media = 0; db->mii.full_duplex = 0; db->mii.dev = ndev; db->mii.mdio_read = dm9000_phy_read; db->mii.mdio_write = dm9000_phy_write; #if defined(CONFIG_ARCH_S3C2410) printk("Now use the default MAC address: 08:90:90:90:90:90\n"); mac_src = "friendly-arm"; ndev->dev_addr[0] = 0x08; ndev->dev_addr[1] = 0x90; ndev->dev_addr[2] = 0x90; ndev->dev_addr[3] = 0x90; ndev->dev_addr[4] = 0x90; ndev->dev_addr[5] = 0x90; #else mac_src = "eeprom"; /* try reading the node address from the attached EEPROM */ for (i = 0; i < 6; i += 2) dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i); if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) { mac_src = "platform data"; memcpy(ndev->dev_addr, pdata->dev_addr, 6); } if (!is_valid_ether_addr(ndev->dev_addr)) { /* try reading from mac */ mac_src = "chip"; for (i = 0; i < 6; i++) ndev->dev_addr[i] = ior(db, i+DM9000_PAR); } if (!is_valid_ether_addr(ndev->dev_addr)) dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please " "set using ifconfig\n", ndev->name); #endif platform_set_drvdata(pdev, ndev); ret = register_netdev(ndev); if (ret == 0) printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n", ndev->name, dm9000_type_to_char(db->type), db->io_addr, db->io_data, ndev->irq, ndev->dev_addr, mac_src); return 0; out: #if defined(CONFIG_ARCH_S3C2410) *(volatile unsigned int *)S3C2410_BWSCON = oldval_bwscon; *(volatile unsigned int *)S3C2410_BANKCON4 = oldval_bankcon4; #endif dev_err(db->dev, "not found (%d).\n", ret); dm9000_release_board(pdev, db); free_netdev(ndev); return ret; }
(7)dm9000_probe()函数总结:dm9000_probe()函数的作用是分配ndev(net_device结构)存储空间、填充其成员,并最终将其注册进内核。而db(board_info结构)作为ndev的私有数据也将一并注册进内核。
注1:dm9000_probe()函数在申请内存空间的时候是带有board_info结构体的空间的,
ndev = alloc_etherdev(sizeof(struct board_info))
其申请的内存空间长度实际为:sizeof(struct net_device) + align + (sizeof(struct board_info)。
注2:db(board_info)指针获取的方法是通过ndev(net_device),即db = ndev + sizeof(net_device) + align
db = netdev_priv(ndev); static inline void *netdev_priv(const struct net_device *dev) { return (char *)dev + ((sizeof(struct net_device) + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST); }
④ dm9000_open()函数:在网卡被激活时调用,主要完成的工作有向内核注册中断、复位并初始化dm9000、检查MII接口等。
static int dm9000_open(struct net_device *dev) { board_info_t *db = netdev_priv(dev); unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK; if (netif_msg_ifup(db)) dev_dbg(db->dev, "enabling %s\n", dev->name); /* If there is no IRQ type specified, default to something that * may work, and tell the user that this is a problem */ if (irqflags == IRQF_TRIGGER_NONE) dev_warn(db->dev, "WARNING: no IRQ resource flags set.\n"); irqflags |= IRQF_SHARED; if (request_irq(dev->irq, &dm9000_interrupt, irqflags, dev->name, dev)) return -EAGAIN; /* Initialize DM9000 board */ dm9000_reset(db); dm9000_init_dm9000(dev); /* Init driver variable */ db->dbug_cnt = 0; mii_check_media(&db->mii, netif_msg_link(db), 1); netif_start_queue(dev); dm9000_schedule_poll(db); return 0;
⑤ dm9000_start_xmit():用于启动数据包的发送,负责将上层送过来的skb数据包发送出去
static int dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev) { unsigned long flags; board_info_t *db = netdev_priv(dev); dm9000_dbg(db, 3, "%s:\n", __func__); if (db->tx_pkt_cnt > 1) return 1; spin_lock_irqsave(&db->lock, flags); /* Move data to DM9000 TX RAM */ writeb(DM9000_MWCMD, db->io_addr); (db->outblk)(db->io_data, skb->data, skb->len); dev->stats.tx_bytes += skb->len; db->tx_pkt_cnt++; /* TX control: First packet immediately send, second packet queue */ if (db->tx_pkt_cnt == 1) { /* Set TX length to DM9000 */ iow(db, DM9000_TXPLL, skb->len); iow(db, DM9000_TXPLH, skb->len >> 8); /* Issue TX polling command */ iow(db, DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */ dev->trans_start = jiffies; /* save the time stamp */ } else { /* Second packet */ db->queue_pkt_len = skb->len; netif_stop_queue(dev); } spin_unlock_irqrestore(&db->lock, flags); /* free this SKB */ dev_kfree_skb(skb); return 0; }
⑥ 数据包接收函数:dm9000_rx()
(1)网卡收到数据包后,调用dm9000_rx()完成以下工作
* 判断包是否接收到
* 检查包的状态和长度
* 读取包数据
* 利用读取到的包的实际数据构造skb,并调用netif_rx()将skb送到上层协议处理。
(2)dm9000_rx()中构造skb,并且递交到上层处理的代码
static void dm9000_rx(struct net_device *dev) { ...
do { ...
/* Move data from DM9000 */ if (GoodPacket && ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) { skb_reserve(skb, 2); rdptr = (u8 *) skb_put(skb, RxLen - 4); /* Read received packet from RX SRAM */ (db->inblk)(db->io_data, rdptr, RxLen); dev->stats.rx_bytes += RxLen; /* Pass to upper layer */ skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->stats.rx_packets++; } else { /* need to dump the packet's data */ (db->dumpblk)(db->io_data, RxLen); } } while(rxbyte == DM9000_PKT_RDY) }
⑦ 数据包发送函数:dm9000_tx_done()
(1)DM9000数据包发送完整过程:
* 在发送一个包之前,将包中的有效数据通过MWCMD寄存器,写入TX缓冲区
* 如果待发送包是第一个包,则直接启动包发送:
* 如果待发送包不是第一个包,则暂不发送,而是记录包长度和校验控制位,并停止发送队列
(注:以上三步在dm9000_start_xmit()方法中实现的)
* 如果设置了IMR(中断屏蔽)寄存器的PTM位,则当数据发送完成后产生中断,并使ISR(中断状态)寄存器的PTS位被设置。因此可以在中断处理函数中判断中断,执行剩余的发送操作
(注:以下三步在dm9000_tx_done()中完成)
* 读取NSR(网络状态)寄存器获取发送的状态,根据NSR_TX2END、NSR_TX1END为来判断是否进行接下来的处理
* 将待发送数据包计数(db->tx_pkt_cnt)减1,且检查db->tx_pkt_cnt是否大于0。如果大于0,则表明还有数据包要发送,将待发送包的长度写入TXPLL和TXPLH寄存器,并通过TCR寄存器请求发送
* 调用函数netif_wake_queue()通知内核可以将待发送的数据包加入发送队列(将skb包通过MWCMD寄存器,写入TX缓冲区)
(2)dm9000_tx_done()函数实现如下:
static void dm9000_tx_done(struct net_device *dev, board_info_t *db) { int tx_status = ior(db, DM9000_NSR); /* Got TX status */ if (tx_status & (NSR_TX2END | NSR_TX1END)) { /* One packet sent complete */ db->tx_pkt_cnt--; dev->stats.tx_packets++; if (netif_msg_tx_done(db)) dev_dbg(db->dev, "tx done, NSR %02x\n", tx_status); /* Queue packet check & send */ if (db->tx_pkt_cnt > 0) { iow(db, DM9000_TXPLL, db->queue_pkt_len); iow(db, DM9000_TXPLH, db->queue_pkt_len >> 8); iow(db, DM9000_TCR, TCR_TXREQ); dev->trans_start = jiffies; } netif_wake_queue(dev); } }
⑧ 中断处理函数:dm9000_interrupt()
(1)Linux源码中的DM9000驱动采用中断方式实现,触发中断的时机发生在:接收到一个数据包后、发送完一个数据包后及连接状态改变之后(DM9000E不支持连接状态改变中断)
(2)中断处理函数在设备执行open方法时被注册进内核,dm9000_interrupt()源码如下
static irqreturn_t dm9000_interrupt(int irq, void *dev_id) { struct net_device *dev = dev_id; board_info_t *db = netdev_priv(dev); int int_status; unsigned long flags; u8 reg_save; dm9000_dbg(db, 3, "entering %s\n", __func__); /* A real interrupt coming */ /* holders of db->lock must always block IRQs */ spin_lock_irqsave(&db->lock, flags); /* Save previous register address */ reg_save = readb(db->io_addr); /* Disable all interrupts */ iow(db, DM9000_IMR, IMR_PAR); /* Got DM9000 interrupt status */ int_status = ior(db, DM9000_ISR); /* Got ISR */ iow(db, DM9000_ISR, int_status); /* Clear ISR status */ if (netif_msg_intr(db)) dev_dbg(db->dev, "interrupt status %02x\n", int_status); /* Received the coming packet */ if (int_status & ISR_PRS) dm9000_rx(dev); /* Trnasmit Interrupt check */ if (int_status & ISR_PTS) dm9000_tx_done(dev, db); if (db->type != TYPE_DM9000E) { if (int_status & ISR_LNKCHNG) { /* fire a link-change request */ schedule_delayed_work(&db->phy_poll, 1); } } /* Re-enable interrupt mask */ iow(db, DM9000_IMR, db->imr_all); /* Restore previous register address */ writeb(reg_save, db->io_addr); spin_unlock_irqrestore(&db->lock, flags); return IRQ_HANDLED; }
6. Mini2440的DM9000网卡驱动移植
① 由于Linux2.6.29内核已经有DM9000完整的驱动,我们只需在板级初始化文件中添加DM9000网卡的平台设备。
② 在板级初始化文件arch/arm/mach-s3c2440/mack-smdk2440.c中添加如下内容:
#include <linux/dm9000.h> #define MACH_SMDK2440_DM9000_BASE (S3C2410_CS4) static struct resource smdk2440_dm9000_resource[] = { [0] = { .start = MACH_SMDK2440_DM9000_BASE, .end = MACH_SMDK2440_DM9000_BASE + 3, .flags = IORESOURCE_MEM, }, [1] = { .start = MACH_SMDK2440_DM9000_BASE + 4, .end = MACH_SMDK2440_DM9000_BASE + 7, .flags = IORESOURCE_MEM, }, [2] = { .start = IRQ_EINT7, .end = IRQ_EINT7, .flags = (IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE), } }; static struct dm9000_plat_data smdk2440_dm9000_pdata = { .flags = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM), }; static struct platform_device smdk2440_device_eth = { .name = "dm9000", .id = -1, .num_resources = ARRAY_SIZE(smdk2440_dm9000_resource), .resource = smdk2440_dm9000_resource, .dev = { .platform_data = &smdk2440_dm9000_pdata, } }; static struct platform_device *smdk2440_devices[] __initdata = { &s3c_device_usb, &s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c0, &s3c_device_iis, &smdk2440_device_eth, };