DM9000 网卡驱动程序分析

平台:MINI2440

系统:Linux-2.6.36.2

mach-s3c2410/include/mach/map.h:

#define S3C2410_CS4 (0x20000000)  //AEN接nGCS4,BANK4

mach-mini2440.c:

#define MACH_MINI2440_DM9K_BASE (S3C2410_CS4 + 0x300)   //TXD[2:0]悬空
static struct resource mini2440_dm9k_resource[] = {
        [0] = {
                .start = MACH_MINI2440_DM9K_BASE,
                .end   = MACH_MINI2440_DM9K_BASE + 3,
                .flags = IORESOURCE_MEM
        },
        [1] = {
                .start = MACH_MINI2440_DM9K_BASE + 4,
                .end   = MACH_MINI2440_DM9K_BASE + 7,
                .flags = IORESOURCE_MEM
        },
        [2] = {
                .start = IRQ_EINT7,   //中断使用了EINT7.
                .end   = IRQ_EINT7,
                .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
        }
};

DM9000平台设备的定义:

static struct dm9000_plat_data mini2440_dm9k_pdata = {
        .flags          = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM),
};

static struct platform_device mini2440_device_eth = {
        .name           = "dm9000",
        .id             = -1,
        .num_resources  = ARRAY_SIZE(mini2440_dm9k_resource),
        .resource       = mini2440_dm9k_resource,
        .dev            = {
                .platform_data  = &mini2440_dm9k_pdata,
        },
};

板子初始化时将平台设备驱动加载到总线上:

static struct platform_device *mini2440_devices[] __initdata = {
	&s3c_device_usb,
	&s3c_device_rtc,
	&s3c_device_lcd,
	&s3c_device_wdt,
	&s3c_device_i2c0,
	&s3c_device_iis,
	&mini2440_device_eth,
	&s3c24xx_uda134x,
	&s3c_device_nand,
	&s3c_device_sdi,
	&s3c_device_usbgadget,
};

dm9000.c:platform_driver与重要操作函数:

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,
};

net_device_ops与重要操作函数:

static const struct net_device_ops dm9000_netdev_ops = {
	.ndo_open		= dm9000_open,
	.ndo_stop		= dm9000_stop,
	.ndo_start_xmit		= dm9000_start_xmit,
	.ndo_tx_timeout		= dm9000_timeout,
	.ndo_set_multicast_list	= dm9000_hash_table,
	.ndo_do_ioctl		= dm9000_ioctl,
	.ndo_change_mtu		= eth_change_mtu,
	.ndo_validate_addr	= eth_validate_addr,
	.ndo_set_mac_address	= eth_mac_addr,
#ifdef CONFIG_NET_POLL_CONTROLLER
	.ndo_poll_controller	= dm9000_poll_controller,
#endif
};

ethtool_ops:

static const struct ethtool_ops dm9000_ethtool_ops = {  //查询与设置网卡的参数
	.get_drvinfo		= dm9000_get_drvinfo,
	.get_settings		= dm9000_get_settings,
	.set_settings		= dm9000_set_settings,
	.get_msglevel		= dm9000_get_msglevel,
	.set_msglevel		= dm9000_set_msglevel,
	.nway_reset		= dm9000_nway_reset,
	.get_link		= dm9000_get_link,
 	.get_eeprom_len		= dm9000_get_eeprom_len,
 	.get_eeprom		= dm9000_get_eeprom,
 	.set_eeprom		= dm9000_set_eeprom,
};

board_info结构体,内容是与芯片相关的信息:

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;

注册平台驱动:

static int __init dm9000_init(void)
{
	printk(KERN_INFO "%s Ethernet Driver, V%s/n", CARDNAME, DRV_VERSION);
	return platform_driver_register(&dm9000_driver);
}

完成match之后执行Probe函数,获得资源信息,最终注册网络设备。

dm9000_remove:在卸载时使用,释放内存空间:

static int __devexit dm9000_drv_remove(struct platform_device *pdev)
{
	struct net_device *ndev = platform_get_drvdata(pdev);

	platform_set_drvdata(pdev, NULL);

	unregister_netdev(ndev);
	dm9000_release_board(pdev, (board_info_t *) netdev_priv(ndev));
	free_netdev(ndev);		/* free device structure */

	dev_dbg(&pdev->dev, "released and freed device/n");
	return 0;
}

dm9000_suspend:并没有将网络设备从内核移除,只是标志设备为removed状态,并设置挂起标志位,最后关闭设备:

static int dm9000_drv_suspend(struct platform_device *dev, pm_message_t state)
{
	struct net_device *ndev = platform_get_drvdata(dev);
	board_info_t *db;

	if (ndev) {
		db = netdev_priv(ndev);
		db->in_suspend = 1;

		if (netif_running(ndev)) {
			netif_device_detach(ndev);
			dm9000_shutdown(ndev);
		}
	}
	return 0;
}

dm9000_resume:

static int dm9000_drv_resume(struct platform_device *dev)
{
	struct net_device *ndev = platform_get_drvdata(dev);
	board_info_t *db = netdev_priv(ndev);

	if (ndev) {

		if (netif_running(ndev)) {
			dm9000_reset(db);
			dm9000_init_dm9000(ndev);
			netif_device_attach(ndev);//标志为attach状态。
		}
		db->in_suspend = 0;
	}
	return 0;
}

dm9000_open:向内核注册中断:

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_stop:

static int dm9000_stop(struct net_device *ndev)
{
	board_info_t *db = netdev_priv(ndev);

	if (netif_msg_ifdown(db))
		dev_dbg(db->dev, "shutting down %s\n", ndev->name);
	cancel_delayed_work_sync(&db->phy_poll);
	netif_stop_queue(ndev);
	netif_carrier_off(ndev);
	free_irq(ndev->irq, ndev);
	dm9000_shutdown(ndev);
	return 0;
}

dm9000_start_xmit():发送数据包函数:

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 NETDEV_TX_BUSY;
        /*获得自旋锁*/
	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);//将skbuffer中的data写入DM9000的TX RAM,并计数
	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_timeout():超时调用:

static void dm9000_timeout(struct net_device *dev)
{
	board_info_t *db = netdev_priv(dev);
	u8 reg_save;
	unsigned long flags;

	/* Save previous register address */   //保存寄存器地址
	reg_save = readb(db->io_addr);
	spin_lock_irqsave(&db->lock, flags);

	netif_stop_queue(dev);                 //停止队列
	dm9000_reset(db);
	dm9000_init_dm9000(dev);               //重启并初始化DM9000
	/* We can accept TX packets again */
	dev->trans_start = jiffies;
	netif_wake_queue(dev);                 //唤醒队列

	/* Restore previous register address */  //恢复寄存器地址
	writeb(reg_save, db->io_addr);
	spin_unlock_irqrestore(&db->lock, flags);
}

dm9000_hash_table():设置组播地址:

static void dm9000_hash_table(struct net_device *dev)
{
	board_info_t *db = netdev_priv(dev);
	struct dev_mc_list *mcptr = dev->mc_list;
	int mc_cnt = dev->mc_count;
	int i, oft;
	u32 hash_val;
	u16 hash_table[4];
	u8 rcr = RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN;
	unsigned long flags;
	dm9000_dbg(db, 1, "entering %s/n", __func__);
	spin_lock_irqsave(&db->lock, flags);
	for (i = 0, oft = DM9000_PAR; i < 6; i++, oft++)
		iow(db, oft, dev->dev_addr[i]);
	/* Clear Hash Table */
	for (i = 0; i < 4; i++)
		hash_table[i] = 0x0;
	/* broadcast address */
	hash_table[3] = 0x8000;
	if (dev->flags & IFF_PROMISC)
		rcr |= RCR_PRMSC;

	if (dev->flags & IFF_ALLMULTI)
		rcr |= RCR_ALL;
	/* the multicast address in Hash Table : 64 bits */
	for (i = 0; i < mc_cnt; i++, mcptr = mcptr->next) {
		hash_val = ether_crc_le(6, mcptr->dmi_addr) & 0x3f;
		hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16);
	}
	/* Write the hash table to MAC MD table */
	for (i = 0, oft = DM9000_MAR; i < 4; i++) {
		iow(db, oft++, hash_table[i]);
		iow(db, oft++, hash_table[i] >> 8);
	}
	iow(db, DM9000_RCR, rcr);
	spin_unlock_irqrestore(&db->lock, flags);
}

dm9000_rx():接收函数:

static void dm9000_rx(struct net_device *dev)
{
	board_info_t *db = netdev_priv(dev);
	struct dm9000_rxhdr rxhdr;
	struct sk_buff *skb;
	u8 rxbyte, *rdptr;
	bool GoodPacket;
	int RxLen;
	/* Check packet ready or not */
	do {
		ior(db, DM9000_MRCMDX);	/* Dummy read */
		/* Get most updated data */
		rxbyte = readb(db->io_data);
		/* Status check: this byte must be 0 or 1 */
		if (rxbyte > DM9000_PKT_RDY) {
			dev_warn(db->dev, "status check fail: %d/n", rxbyte);
			iow(db, DM9000_RCR, 0x00);	/* Stop Device */
			iow(db, DM9000_ISR, IMR_PAR);	/* Stop INT request */
			return;
		}
		if (rxbyte != DM9000_PKT_RDY)
			return;
		/* A packet ready now  & Get status/length */
		GoodPacket = true;
		writeb(DM9000_MRCMD, db->io_addr);
		(db->inblk)(db->io_data, &rxhdr, sizeof(rxhdr));//读四个字节到rxhdr变量
		RxLen = le16_to_cpu(rxhdr.RxLen);
		if (netif_msg_rx_status(db))
			dev_dbg(db->dev, "RX: status %02x, length %04x/n",
				rxhdr.RxStatus, RxLen);
		/* Packet Status check */
		if (RxLen < 0x40) {
			GoodPacket = false;
			if (netif_msg_rx_err(db))
				dev_dbg(db->dev, "RX: Bad Packet (runt)/n");
		}
		if (RxLen > DM9000_PKT_MAX) {
			dev_dbg(db->dev, "RST: RX Len:%x/n", RxLen);
		}
		/* rxhdr.RxStatus is identical to RSR register. */
		if (rxhdr.RxStatus & (RSR_FOE | RSR_CE | RSR_AE |
				      RSR_PLE | RSR_RWTO |
				      RSR_LCS | RSR_RF)) {
			GoodPacket = false;
			if (rxhdr.RxStatus & RSR_FOE) {
				if (netif_msg_rx_err(db))
					dev_dbg(db->dev, "fifo error/n");
				dev->stats.rx_fifo_errors++;
			}
			if (rxhdr.RxStatus & RSR_CE) {
				if (netif_msg_rx_err(db))
					dev_dbg(db->dev, "crc error/n");
				dev->stats.rx_crc_errors++;
			}
			if (rxhdr.RxStatus & RSR_RF) {
				if (netif_msg_rx_err(db))
					dev_dbg(db->dev, "length error/n");
				dev->stats.rx_length_errors++;
			}
		}
		/* Move data from DM9000 */   //将RX SRAM中的data放到sk_buffer。
		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_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);      //调用done函数

	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;
}

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 */  //读取NSR的状态,得知发送的状态
	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);
	}
}
时间: 2024-11-05 22:02:10

DM9000 网卡驱动程序分析的相关文章

Linux DM9000网卡驱动程序完全分析

Linux DM9000网卡驱动程序完全分析http://blog.csdn.net/ypoflyer/article/details/6209922

S3C2440实现dm9000网卡驱动程序移植

20150419 S3C2440实现dm9000网卡驱动程序移植 2015-04-19 Lover雪儿 首先附上厂家提供的完整的dm9000程序: 1 /* 2 3 dm9ks.c: Version 2.08 2007/02/12 4 5 A Davicom DM9000/DM9010 ISA NIC fast Ethernet driver for Linux. 6 7 This program is free software; you can redistribute it and/or

[国嵌攻略][136][DM9000网卡驱动深度分析]

网卡初始化 1.分配描述结构,alloc_etherdev 2.获取平台资源,platform_get_resource 2.1.在s3c_dm9k_resource中有相关的资源 2.2.add地址由CS4和ADD2决定,是20000000 2.3.dat地址由CS4和ADD2决定,是20000004 2.4.中断资源是EINT7 3.虚拟地址映射,ioremap 4.读取芯片类型 5.设置操作函数集 6.读取MAC地址 7.注册网卡驱动,register_netdev 8.启动发送队列,ne

DM9000C网卡驱动程序编写与测试

一般网卡驱动程序厂商会给我们提供一份模板驱动,我们的工作就是需要根据自己的需要更改这个模板驱动 1.DM9000C的硬件连接 硬件连接图如下所示:它接在S3C2440的BANK4内存控制器上,它只占用8个字节的长度,并且是16bit的位宽. 下面介绍一下DM9000C的主要引脚的功能:SD0-SD15位16bit的数据引脚接口:IOR为读使能信号,低电平有效:IOW为写使能信号,低电平有效:CS为片选信号,低电平有效:CMD为数据与索引选择信号,高电平表数据,低电平表索引,它连接到S3C2440

驱动11.网卡驱动程序

1 网络传输的简介 (1)接收过程,如上图,网络上的数据包到达网卡后,网卡产生中断,然后设备驱动层收到中断后,开始进行网络包的接收,接收完之后调用一个netif_rx函数交给网络协议层(层次结构上图一),然后就是一层一层的网上传到用户空间了. (2)发送过程,从用户空间过来的数据包,经过层层穿越之后,到达网络协议层,然后调用一个dev_queue_xmit()函数之后就不管了,剩下的交给驱动层经过处理后,使用函数hard_start_xmit()函数发送,然后硬件上网卡开始发送数据包了. 2 d

《 转》Linux 网卡驱动程序对 ethtool 的支持和实现

Linux 的一个显著特点就是其强大的网络功能,Linux 几乎支持所有的网络协议,并在这些协议基础上提供了丰富的应用.对 Linux 网络管理的重要性不言而喻,这些管理依赖于网络工具,比如最常用的 ifconfig,route,ip,ethtool 等,其中 ethtool 提供了强大的网卡及网卡驱动管理能力,其具体的实现框架和网络驱动程序及网络硬件关系紧密,容易修改和扩展,能够为 Linux 网络开发人员和管理人员提供对网卡硬件,驱动程序和网络协议栈的设置,查看以及及调试等功能. 从典型的以

Linux中块设备驱动程序分析

基于<Linux设备驱动程序>书中的sbull程序以对Linux块设备驱动总结分析. 开始之前先来了解这个块设备中的核心数据结构: struct sbull_dev { int size;                       /* Device size in sectors */ u8 *data;                       /* The data array */ short users;                    /* How many users

安装或更新CentOS6.7平台的网卡驱动程序

基于Linux平台安装或更新网卡驱动程序与Windows平台相差不大,首先查阅出主机网卡的具体型号.Windows平台可以借助鲁大师等硬件检测工具查看网卡,Linux平台有适用的命令lspci.ethtool查看. 在CentOS6.7平台下使用命令lspci | grep -i net查看网卡具体型号 可以查看到该主机网卡为Intel 82545EM 千兆以太网卡 如果是更新网卡驱动,我们可以使用命令ethtool -i eth0查看目前网卡驱动程序版本信息 可查看到驱动程序名称.版本号.固件

LED驱动程序分析

混杂设备 LED驱动程序分析 /******************************* * *杂项设备驱动:miscdevice *majior=10; * * *****************************/ #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/types.h> #include <lin