"Linux driver: DM9000 network card driver analysis"

I. Introduction

The framework of the network device driver and the general process of building the device driver were analyzed before, and now the understanding of the network device driver is further strengthened through the analysis of the DM9000C network card driver.

Second, the hardware circuit

2.1 DM9000C

SD0~SD15: data/address transmission.
CMD: This pin determines whether to access the data register or the index register of DM9000C. Write the address transmitted through LDATA0 ~ LDATA15 to the index register of DM9000C, or read data from the data register of DM9000C and transmit it to the memory controller of s3c2440 through LDATA0 ~ LDATA15.
INT: interrupt
CS: chip select
RST: reset
insert image description here

2.2 S3c2440

The interrupt number of DM9000C is EINT7
insert image description here
insert image description here

2.3 S3c2440 memory controller

When operating addresses before 0x2000 0000 ~ 0x2800 0000, chip select nGCS4.
The base address is 0x2000 0000. When operating the index register of DM9000C, LADDR2 (CMD) needs to be 0, that is, to operate the index register of DM9000C is to access the address 0x2000 0000. When operating the data register of DM9000C, LADDR2 (CMD) needs to be 1, that is, to operate the data register of DM9000C is to access the address (0x2000 0000 + 4).
insert image description here

The memory controller needs to be configured, and the chip select nGCS4 corresponds to BANK4. That is, registers BWSCON (0x48000000) and BANKCON4 (0x48000014) need to be configured.
insert image description here

Three, probe function analysis

static int __init dm9dev9000c_init(void)
{
    
    
	volatile unsigned long *bwscon; // 0x48000000
	volatile unsigned long *bankcon4; // 0x48000014
	unsigned long val;

	iobase = (int)ioremap(0x20000000, 1024);  
	irq    = IRQ_EINT7;



	/* 设置S3C2440的memory controller */
	bwscon   = ioremap(0x48000000, 4);
	bankcon4 = ioremap(0x48000014, 4);

	/* DW4[17:16]: 01-16bit
	 * WS4[18]   : 0-WAIT disable
	 * ST4[19]   : 0 = Not using UB/LB (The pins are dedicated nWBE[3:0])
	 */
	val = *bwscon;
	val &= ~(0xf<<16);
	val |= (1<<16);
	*bwscon = val;

	/*
	 * Tacs[14:13]: 发出片选信号之前,多长时间内要先发出地址信号
	 *              DM9000C的片选信号和CMD信号可以同时发出,
	 *              所以它设为0
	 * Tcos[12:11]: 发出片选信号之后,多长时间才能发出读信号nOE
	 *              DM9000C的T1>=0ns, 
	 *              所以它设为0
	 * Tacc[10:8] : 读写信号的脉冲长度, 
	 *              DM9000C的T2>=10ns, 
	 *              所以它设为1, 表示2个hclk周期,hclk=100MHz,就是20ns
	 * Tcoh[7:6]  : 当读信号nOE变为高电平后,片选信号还要维持多长时间
	 *              DM9000C进行写操作时, nWE变为高电平之后, 数据线上的数据还要维持最少3ns
	 *              DM9000C进行读操作时, nOE变为高电平之后, 数据线上的数据在6ns之内会消失
	 *              我们取一个宽松值: 让片选信号在nOE放为高电平后,再维持10ns, 
	 *              所以设为01
	 * Tcah[5:4]  : 当片选信号变为高电平后, 地址信号还要维持多长时间
	 *              DM9000C的片选信号和CMD信号可以同时出现,同时消失
	 *              所以设为0
	 * PMC[1:0]   : 00-正常模式
	 *
	 */
	*bankcon4 = (1<<8)|(1<<6);	/* 对于DM9000C可以设Tacc为1, 对于DM9000E,Tacc要设大一点,比如最大值7  */
	//*bankcon4 = (7<<8)|(1<<6);  /* MINI2440使用DM9000E,Tacc要设大一点 */

	iounmap(bwscon);
	iounmap(bankcon4);
    
	switch(mode) {
    
    
		case DM9KS_10MHD:
		case DM9KS_100MHD:
		case DM9KS_10MFD:
		case DM9KS_100MFD:
			media_mode = mode;
			break;
		default:
			media_mode = DM9KS_AUTO;
	}
	dmfe_dev = dmfe_probe();
	if(IS_ERR(dmfe_dev))
		return PTR_ERR(dmfe_dev);
	return 0;
}


struct net_device * __init dmfe_probe(void)
{
    
    
	struct net_device *dev;
	int err;
	
	DMFE_DBUG(0, "dmfe_probe()",0);

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
	dev = init_etherdev(NULL, sizeof(struct board_info));
	//ether_setup(dev);		
#else
	// 申请分配一个net_device结构体
	dev= alloc_etherdev(sizeof(struct board_info)); // -> alloc_netdev(sizeof_priv, "eth%d", ether_setup); 
#endif

	if(!dev)
		return ERR_PTR(-ENOMEM);

     	SET_MODULE_OWNER(dev);
	// 设置net_device结构体
	err = dmfe_probe1(dev);
	if (err)
		goto out;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
	// 注册
	err = register_netdev(dev);
	if (err)
		goto out1;
#endif
	return dev;
out1:
	release_region(dev->base_addr,2);
out:
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
	kfree(dev);
#else
	free_netdev(dev);
#endif
	return ERR_PTR(err);
}

3.1 Read the device ID of DM9000C

.......
		outb(DM9KS_VID_L, iobase);
		id_val = inb(iobase + 4);
		outb(DM9KS_VID_H, iobase);
		id_val |= inb(iobase + 4) << 8;
		outb(DM9KS_PID_L, iobase);
		id_val |= inb(iobase + 4) << 16;
		outb(DM9KS_PID_H, iobase);
		id_val |= inb(iobase + 4) << 24;

		printk(KERN_ERR"line %d <DM9KS> I/O: %x, VID: %x \n", __LINE__, iobase, id_val);
		if (id_val == DM9KS_ID || id_val == DM9010_ID) 
        {
    
    
            .......
        } 
.......

3.2 Set net_device structure

......
			/* driver system function */				
			dev->base_addr 		= iobase;      			// 设置设备基地址
			dev->irq 		= irq;						// 中断号
			dev->open 		= &dmfe_open;				// 提供open接口
			dev->hard_start_xmit 	= &dmfe_start_xmit; // 提供发包接口
			dev->watchdog_timeo	= 5*HZ;					// 设置超时时间
			dev->tx_timeout		= dmfe_timeout;    		// 提供超时处理接口
			dev->stop 		= &dmfe_stop;				// 提供stop接口
			dev->get_stats 		= &dmfe_get_stats;      // 提供获取统计信息接口
			dev->set_multicast_list = &dm9000_hash_table;
			dev->do_ioctl 		= &dmfe_do_ioctl;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,28)
			dev->ethtool_ops = &dmfe_ethtool_ops;
#endif
#ifdef CHECKSUM
			//dev->features |=  NETIF_F_IP_CSUM;
			dev->features |=  NETIF_F_IP_CSUM|NETIF_F_SG;
#endif
......

3.3 Registration Interruption

......
request_irq(dev->irq,&dmfe_interrupt,0,dev->name,dev)
......

static irqreturn_t dmfe_interrupt(int irq, void *dev_id) /* for kernel 2.6.20*/
{
    
    
	struct net_device *dev = dev_id;
	board_info_t *db;
	int int_status,i;
	u8 reg_save;

	DMFE_DBUG(0, "dmfe_interrupt()", 0);

	/* A real interrupt coming */
	db = (board_info_t *)dev->priv;
	spin_lock(&db->lock);

	/* Save previous register address */
	reg_save = inb(db->io_addr);

	/* Disable all interrupt */
	iow(db, DM9KS_IMR, DM9KS_DISINTR); 

	
	// 获取网卡中断的类型
	int_status = ior(db, DM9KS_ISR);		/* Got ISR */
	iow(db, DM9KS_ISR, int_status);		/* Clear ISR status */ 

	// 链接状态改变中断
	if (int_status & DM9KS_LINK_INTR) 
	{
    
    
		netif_stop_queue(dev);
		for(i=0; i<500; i++) /*wait link OK, waiting time =0.5s */
		{
    
    
			phy_read(db,0x1);
			if(phy_read(db,0x1) & 0x4) /*Link OK*/
			{
    
    
				/* wait for detected Speed */
				for(i=0; i<200;i++)
					udelay(1000);
				/* set media speed */
				if(phy_read(db,0)&0x2000) db->Speed =100;
				else db->Speed =10;
				break;
			}
			udelay(1000);
		}
		netif_wake_queue(dev);
		//printk("[INTR]i=%d speed=%d\n",i, (int)(db->Speed));	
	}

	// 接收数据中断
	if (int_status & DM9KS_RX_INTR) 
		dmfe_packet_receive(dev);    // -> netif_rx(skb); 将接收到的数据传送到上层网络层

	// 检验中断
	if (int_status & DM9KS_TX_INTR)
		dmfe_tx_done(0);
	
	if (db->cont_rx_pkt_cnt>=CONT_RX_PKT_CNT)
	{
    
    
		iow(db, DM9KS_IMR, 0xa2);
	}
	else
	{
    
    
		/* Re-enable interrupt mask */ 
		iow(db, DM9KS_IMR, DM9KS_REGFF);
	}
	
	/* Restore previous register address */
	outb(reg_save, db->io_addr); 

	spin_unlock(&db->lock); 
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
	return IRQ_HANDLED;
#endif
}    

Four, summary

The general process of building a network device driver (device driver function layer)

  • Call the alloc_netdev interface to apply for allocation of a net_device structure.
  • Set the net_device structure and provide at least one sending function.
  • Apply for a network card related interrupt according to the hardware circuit, receive the data sent by other devices received by the network card during the interrupt, and send it to the upper layer for processing through the netif_rx function.
  • Call the register_netdev interface to register the driver.

Guess you like

Origin blog.csdn.net/qq_40709487/article/details/127150185