DM 9000 NICドライバ分析


プラットフォーム:MINI 2440
システム: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,
        }
};
DM 9000プラットフォームデバイスの定義:
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
", 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.
"); 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
", 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);
	}
}