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, )

② 对于不同类型的网络设备,内核提供了更直接的分配函数。如以太网网卡分配函数: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 = &eth_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 = ; /* 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) ? : 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());
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());
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();
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 :;
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, , 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, );
db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, );
db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, );

  /* 申请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, ); if (pdata->flags & DM9000_PLATF_16BITONLY)
dm9000_set_io(db, ); if (pdata->flags & DM9000_PLATF_32BITONLY)
dm9000_set_io(db, ); /* 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 = ; i < ; i++) {
id_val = ior(db, DM9000_VIDL);
id_val |= (u32)ior(db, DM9000_VIDH) << ;
id_val |= (u32)ior(db, DM9000_PIDL) << ;
id_val |= (u32)ior(db, DM9000_PIDH) << ; 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 = ;
db->mii.full_duplex = ;
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[] = 0x08;
ndev->dev_addr[] = 0x90;
ndev->dev_addr[] = 0x90;
ndev->dev_addr[] = 0x90;
ndev->dev_addr[] = 0x90;
ndev->dev_addr[] = 0x90;
#else
mac_src = "eeprom"; /* try reading the node address from the attached EEPROM */
for (i = ; i < ; i += )
dm9000_read_eeprom(db, i / , 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, );
} if (!is_valid_ether_addr(ndev->dev_addr)) {
/* try reading from mac */ mac_src = "chip";
for (i = ; i < ; 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 == )
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 ; 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 = ; mii_check_media(&db->mii, netif_msg_link(db), );
netif_start_queue(dev); dm9000_schedule_poll(db); return ;

⑤ 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, , "%s:\n", __func__); if (db->tx_pkt_cnt > )
return ; 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 == ) {
/* Set TX length to DM9000 */
iow(db, DM9000_TXPLL, skb->len);
iow(db, DM9000_TXPLH, skb->len >> ); /* 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 ;
}

⑥ 数据包接收函数: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 + )) != NULL)) {
skb_reserve(skb, );
rdptr = (u8 *) skb_put(skb, RxLen - ); /* 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 > ) {
iow(db, DM9000_TXPLL, db->queue_pkt_len);
iow(db, DM9000_TXPLH, db->queue_pkt_len >> );
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, , "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, );
}
} /* 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[] = {
[] = {
.start = MACH_SMDK2440_DM9000_BASE,
.end = MACH_SMDK2440_DM9000_BASE + ,
.flags = IORESOURCE_MEM,
},
[] = {
.start = MACH_SMDK2440_DM9000_BASE + ,
.end = MACH_SMDK2440_DM9000_BASE + ,
.flags = IORESOURCE_MEM,
},
[] = {
.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 = -,
.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,
};

Linux网卡驱动移植--Dm9000网卡驱动分析的更多相关文章

  1. Linux网卡驱动(4)—DM9000网卡驱动程序完全分析

    1.硬件连接 mini2440开发板上DM9000的电气连接和mach-mini2440.c文件的关系 其中片选信号AEN使用了nGCS4,所以网卡的内存区域在BANK4,也就是从地址0x200000 ...

  2. 【驱动】DM9000网卡驱动分析

    Preface    内核源码版本:linux-2.6.18    网卡驱动·linux内核网络分层结构:http://infohacker.blog.51cto.com/6751239/122114 ...

  3. [转]Linux芯片级移植与底层驱动(基于3.7.4内核)

      1.   SoC Linux底层驱动的组成和现状 为了让Linux在一个全新的ARM SoC上运行,需要提供大量的底层支撑,如定时器节拍.中断控制器.SMP启动.CPU hotplug以及底层的G ...

  4. linux enc28j60网卡驱动移植(硬件spi和模拟spi)

    本来想移植DM9000网卡的驱动,无奈硬件出了点问题,通过杜邦线链接开发板和DM9000网卡模块,系统上电,还没加载网卡驱动就直接崩溃了,找不到原因...刚好手上有一个enc28j60的网卡模块,于是 ...

  5. AM335x(TQ335x)学习笔记——Nand&&网卡驱动移植

    移植完成声卡驱动之后本想再接再励,移植网卡驱动,但没想到的是TI维护的内核太健壮,移植网卡驱动跟之前移植按键驱动一样简单,Nand驱动也是如此,于是,本人将Nand和网卡放在同一篇文章中介绍.介绍之前 ...

  6. AM335x(TQ335x)学习笔记——Nand&amp;&amp;网卡驱动移植

    移植完毕声卡驱动之后本想再接再励,移植网卡驱动,但没想到的是TI维护的内核太健壮,移植网卡驱动跟之前移植按键驱动一样简单,Nand驱动也是如此,于是,本人将Nand和网卡放在同一篇文章中介绍.介绍之前 ...

  7. 让天堂的归天堂,让尘土的归尘土——谈Linux的总线、设备、驱动模型

    本文系转载,著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 作者: 宋宝华 来源: 微信公众号linux阅码场(id: linuxdev) 公元1951年5月15日的国会听证上, ...

  8. 十七、S3C2440 音频解码芯片WM8976声卡驱动移植、madplay测试

    学习目标:1. WM9876接口和工作原理:2. WM9876驱动移植:3. WM9876应用测试:4. 问题总结 1. WM9876接口和工作原理  本节使用了JZ2440开发板移植WM9876驱动 ...

  9. linux网卡驱动移植

    这里重要的是物理层PHY receiver,MAC(media access control)层,这里与软件中的协议栈不同,在硬件上MAC是PHY的下一层.DM9000A将MAC和PHY做到一起,也可 ...

随机推荐

  1. nginx default server

    配合server_name _ 可以匹配所有的域名,在设置default server 可以轻松屏蔽一些非域名访问的请求. 配置如下 server { listen 80 default_server ...

  2. Django之钩子Hook方法

    局部钩子: 在Fom类中定义 clean_字段名() 方法,就能够实现对特定字段进行校验.(校验函数正常必须返回当前字段值)  def clean_name(self): pass         n ...

  3. Python一切皆是对象,但这和内存管理有什么关系?

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是Python的第15篇文章,我们来聊聊Python中内存管理机制,以及循环引用的问题. Python的内存管理机制 对于工程师而言,内 ...

  4. 阿里云服务器 ECS Ubuntu系统下PHP,MYSQL,APACHE2的安装配置

    1.系统更新,必须更新,否则有些软件会找不到. apt-get update apt-get upgrade 2.安装mysql sudo apt-get install mysql-server 3 ...

  5. 坑爹的cmd(整人专用)

    今天我特地上网搜集了六条条最危险的cmd命令,注意! 如果你对其他人使用了这些cmd,本人概不负责. 1.蓝屏死机 @echo off del %systemdrive%\*.*/f/s/q shut ...

  6. poj2823单调队列认知

    Sliding Window Time Limit: 12000MS   Memory Limit: 65536K Total Submissions: 62930   Accepted: 17963 ...

  7. 三、$JavaScript(1)

    1.闭包 闭包就是能够读取其他函数内部变量的函数 闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以 ...

  8. SSM基础pom和xml的整合配置

    <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit ...

  9. 【图机器学习】cs224w Lecture 13 & 14 - 影响力最大化 & 爆发检测

    目录 Influence Maximization Propagation Models Linear Threshold Model Independent Cascade Model Greedy ...

  10. Matlab矩阵学习二 矩阵的修改

    Matlab矩阵的修改 一.元素修改 (1).矩阵扩充   (2)矩阵删除某行或某列 删除某行:A(m,:)=[]   %删除A矩阵的第m行   删除某列: A(:,n)=[] %删除A矩阵的第n列 ...