⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ve100.c

📁 Linux 下的ve100网卡驱动
💻 C
📖 第 1 页 / 共 3 页
字号:
		if(!(nic->rxs = kcalloc(count,sizeof(struct rx),GFP_ATOMIC)))
			return -ENOMEM;
		for(rx = nic->rxs,i=0; i < count; i++,rx++)
		{
				rx->next = (i+1 <count) ? rx+1 : nic->rxs;
				rx->prev = (i == 0) ? nic->rxs + count -1 : rx - 1;
				if(ve100_rx_alloc_skb(nic,rx))
				{
						ve100_rx_clean_list(nic);
						return -ENOMEM;
				}
		}
		nic->rx_to_use = nic->rx_to_clean = nic->rxs;
		nic->ru_running = RU_SUSPENDED;
		return 0;
}
/* interrupt */
static irqreturn_t ve100_intr(int irq,void* id)
{
		struct net_device* ndev = id;
		struct nic* nic = netdev_priv(ndev);
		u8 stat_ack = ioread8(&nic->csr->scb.stat_ack);
		
		if(stat_ack == stat_ack_not_ours || stat_ack == stat_ack_not_present)
			return IRQ_NONE;
		
		/* ack interrupt */
		iowrite8(stat_ack,&nic->csr->scb.stat_ack);
		/* hit receive no resource; restart ru after cleaning */
		if(stat_ack & stat_ack_rnr)
			nic->ru_running = RU_SUSPENDED;
		if(likely(netif_rx_schedule_prep(ndev)))
		{
				ve100_disable_irq(nic);
				__netif_rx_schedule(ndev);
		}
		return IRQ_HANDLED;
}

/* ve100 up */
static int ve100_start(struct nic* nic)
{
		int ret;
		if((ret = ve100_rx_alloc_list(nic)))
		{
			printk("aaa\n");
			return ret;
		}
		if((ret = ve100_alloc_cbs(nic)))
		{	
			printk("bbb\n");
			goto err_rx_clean_list;
		}
		if((ret = ve100_hw_init(nic)))
		{	
			printk("ccc\n");
			goto err_clean_cbs;
		}
		ve100_set_multicast_list(nic->ndev);
		ve100_start_receiver(nic,NULL);
		mod_timer(&nic->watchdog,jiffies);
		if((ret = request_irq(nic->pdev->irq,ve100_intr,IRQF_SHARED,nic->ndev->name,nic->ndev)))
			goto err_no_irq;
			
		/*Lionel 2008 to start the transmission queue */
		netif_wake_queue(nic->ndev);
		/* to enable the poll method */
		netif_poll_enable(nic->ndev);
		/* enable ints _after_ enabling poll, preventing a race between
	 * disable ints+schedule */
	 ve100_enable_irq(nic);
	 return 0;

err_no_irq:
		del_timer_sync(&nic->watchdog);
err_clean_cbs:
		ve100_clean_cbs(nic);
err_rx_clean_list:
		ve100_rx_clean_list(nic);
		return ret;
}
/* ve100 down */
static void ve100_end(struct nic* nic)
{
		/* wait for poll complete */
		netif_poll_disable(nic->ndev);
		netif_stop_queue(nic->ndev);
		ve100_hw_reset(nic);
		free_irq(nic->pdev->irq,nic->ndev);
		del_timer_sync(&nic->watchdog);
		netif_carrier_off(nic->ndev);
		ve100_clean_cbs(nic);
		ve100_rx_clean_list(nic);
}
/* tranmist timeout task */
static void ve100_tx_timeout_task(struct work_struct* work)
{
		struct nic* nic = container_of(work,struct nic,tx_timeout_task);
		struct net_device* ndev = nic->ndev;
		ve100_end(netdev_priv(ndev));
		ve100_start(netdev_priv(ndev));
}

/* blink led */
static void ve100_blink_led(unsigned long data)
{
		struct nic* nic = (struct nic*)data;
		enum led_state
		{
				led_on = 0x1,
				led_off = 0x4,
				led_on_559 = 0x5,
				led_on_557 = 0x7,
				
		};
		nic->leds = (nic->leds & led_on) ? led_off : (nic->mac < mac_82559_D101M) ? led_on_557 : led_on_559;
		mdio_write(nic->ndev,nic->mii.phy_id,MII_LED_CONTROL,nic->leds);
		mod_timer(&nic->blink_timer,jiffies + HZ /4);
}
/* e100_multi */
static void ve100_multi(struct nic* nic,struct cb* cb,struct sk_buff* skb)
{
		struct net_device* ndev = nic->ndev;
		struct dev_mc_list* list = ndev->mc_list;
		u16 i,count = min(ndev->mc_count,Max_Multicast_Addrs);
		
		cb->command = cpu_to_le16(cb_multi);
		cb->u.multi.count = cpu_to_le16(count* ETH_ALEN);
		for(i = 0; list&&i<count; i++,list = list->next)
			memcpy(&cb->u.multi.addr[i * ETH_ALEN],&list->dmi_addr,ETH_ALEN);
}
/* set multicast list */
static void ve100_set_multicast_list(struct net_device* ndev)
{
		struct nic* nic = netdev_priv(ndev);
		if(ndev->flags & IFF_PROMISC)
			nic->flags |= promiscuous;
		else
			nic->flags &= ~promiscuous;
		if(ndev->flags & IFF_ALLMULTI || ndev->mc_count > Max_Multicast_Addrs)
			nic->flags |= multicast_all;
		else
			nic->flags &= ~multicast_all;
		ve100_exec_cb(nic,NULL,ve100_configure);
		ve100_exec_cb(nic,NULL,ve100_multi);
}

/* Adjust inter-frame-spacing (IFS) between two transmits if
	 * we're getting collisions on a half-duplex connection. */
static void ve100_adjust_adaptive_ifs(struct nic* nic,int speed,int duplex)
{
		if(duplex == DUPLEX_HALF)
		{
			u32 prev = nic->adaptive_ifs;
			u32 min_frames = (speed == SPEED_100) ? 1000 : 100;
			
			if((nic->tx_frames /32 < nic->tx_collisions) && (nic->tx_frames > min_frames))
			{
					if(nic->adaptive_ifs < 60)
						nic->adaptive_ifs += 5;
			}
			else if(nic->tx_frames < min_frames)
			{
				if(nic->adaptive_ifs >= 5)
					nic->adaptive_ifs -= 5;
			}
			if(nic->adaptive_ifs != prev)
				ve100_exec_cb(nic,NULL,ve100_configure);
		}
			
}

/* transmit prepare */
static void ve100_xmit_prepare(struct nic* nic,struct cb* cb,struct sk_buff* skb)
{
		cb->command = nic->tx_command;
		if((nic->cbs_avail & ~15) == nic->cbs_avail)
			/*Lionel 2008 If the I bit is set to one, the device generates an interrupt after the execution of the CB is 
finished. If I is not set to one, the CX interrupt will not be generated.*/
			cb->command |= cpu_to_le16(cb_i);
	cb->u.tcb.tbd_array = cb->dma_addr + offsetof(struct cb, u.tcb.tbd);
	cb->u.tcb.tcb_byte_count = 0;
	cb->u.tcb.threshold = nic->tx_threshold;
	cb->u.tcb.tbd_count = 1;
	cb->u.tcb.tbd.buf_addr = cpu_to_le32(pci_map_single(nic->pdev,
		skb->data, skb->len, PCI_DMA_TODEVICE));
	/* check for mapping failure? */
	cb->u.tcb.tbd.size = cpu_to_le16(skb->len);
}

/* watchdog function */
static void ve100_watchdog(unsigned long data)
{
	struct ethtool_cmd cmd;
	struct nic* nic = (struct nic*)data;
	
	mii_ethtool_gset(&nic->mii,&cmd);
	//mii_link_ok(&nic->mii);
	//netif_carrier_ok(nic->ndev);
	if(mii_link_ok(&nic->mii) && !netif_carrier_ok(nic->ndev))
	{
		printk("link up,%sMbps, %s-duplex\n",cmd.speed == SPEED_100 ? "100":"10",cmd.duplex==DUPLEX_FULL ? "full" : "half");
	}
	else if(!mii_link_ok(&nic->mii) && netif_carrier_ok(nic->ndev))
	{
		printk("link down\n");
	}
	mii_check_link(&nic->mii);
	
	spin_lock_irq(&nic->cmd_lock);
	iowrite8(irq_sw_gen | ioread8(&nic->csr->scb.cmd_high),&nic->csr->scb.cmd_high);
	ve100_write_flush(nic);
	spin_unlock_irq(&nic->cmd_lock);
	
	ve100_update_stats(nic);
	ve100_adjust_adaptive_ifs(nic,cmd.speed,cmd.duplex);
	if(nic->mac <= mac_82557_D100_C)
		ve100_set_multicast_list(nic->ndev);
	if(nic->flags & ich && cmd.speed == SPEED_10 && cmd.duplex==DUPLEX_HALF)
		nic->flags |= ich_10h_workaround;
	else
		nic->flags &= ~ich_10h_workaround;
	
	mod_timer(&nic->watchdog,jiffies + VE100_WATCHDOG_PERIOD);
	
}
/* ve100 asf*/
static int ve100_asf(struct nic* nic)
{
		/* ASF can be enabled from eeprom */
		return((nic->pdev->device >= 0x1050) && (nic->pdev->device <= 0x1057) &&
	   (nic->eeprom[eeprom_config_asf] & eeprom_asf) &&
	   !(nic->eeprom[eeprom_config_asf] & eeprom_gcl) &&
	   ((nic->eeprom[eeprom_smbus_addr] & 0xFF) != 0xFE));
}
/* shutdown ve100 */
static void vd100_shutdown(struct pci_dev* pdev)
{
		struct net_device* ndev = pci_get_drvdata(pdev);
		struct nic* nic = netdev_priv(ndev);
		if(netif_running(ndev))
			netif_poll_disable(nic->ndev);
		del_timer_sync(&nic->watchdog);
		netif_carrier_off(nic->ndev);
		
		if(ve100_asf(nic) | (nic->flags & wol_magic))
		{
			pci_enable_wake(pdev,PCI_D3hot,1);
			pci_enable_wake(pdev,PCI_D3cold,1);
		}
		else
		{
			pci_enable_wake(pdev,PCI_D3hot,0);
			pci_enable_wake(pdev,PCI_D3cold,0);
		}
		pci_disable_device(pdev);
		pci_set_power_state(pdev,PCI_D3hot);
}

static int __devinit ve100_probe(struct pci_dev* pdev,const struct pci_device_id* pdi)
{
		struct net_device *ndev;
		struct nic* nic;
		int ret;
		if(!(ndev = alloc_etherdev(sizeof(struct nic))))
		{
			return -ENOMEM;
		}
		ndev->open = ve100_open;
		ndev->stop = ve100_close;
		ndev->hard_start_xmit = ve100_xmit_frame;
		ndev->get_stats = ve100_get_stats;
		ndev->set_multicast_list = ve100_set_multicast_list;
		ndev->set_mac_address = ve100_set_mac_address;
		ndev->change_mtu = ve100_change_mtu;
		ndev->do_ioctl = ve100_do_ioctl;
		ndev->tx_timeout = ve100_tx_timeout;
		ndev->watchdog_timeo = VE100_WATCHDOG_PERIOD;
		ndev->poll = ve100_poll;
		ndev->weight = VE100_NAPI_WEIGHT;
		strncpy(ndev->name,pci_name(pdev),sizeof(ndev->name) -1);
		
		/*Lionel 2008 initialize the net device and bind to the netcard */
		nic = netdev_priv(ndev);
		nic->ndev = ndev;
		nic->pdev = pdev;
		nic->msg_enable = (1 << debug) - 1;
		pci_set_drvdata(pdev,ndev);
		
		/*Lionel 2008 activate the net device */
		if((ret = pci_enable_device(pdev)))
		{
			printk("Can't enable PCI device, exit!\n");
			goto err_out_free_dev;
		}
		/*Lionel 2008 get flags related to the resource */
		if(!(pci_resource_flags(pdev,0) & IORESOURCE_MEM))
		{
			printk("Can't find proper PCI device, exit!\n");
			ret = -ENODEV;
			goto err_out_disable_pdev;
			
		}
		/* Lionel 2008 get pci resources for 6 registers request of pci */
		if((ret = pci_request_regions(pdev,DRV_NAME)))
		{
			printk("Can't get PCI resources, exit!\n");
			goto err_out_disable_pdev;
		}
		/*set 32bit dma mask */
		if((ret = pci_set_dma_mask(pdev,DMA_32BIT_MASK)))
		{
			printk("DMA config error,exit !\n");
			goto err_out_free_res;
		}
		SET_MODULE_OWNER(ndev);
		SET_NETDEV_DEV(ndev,&pdev->dev);
		
		if (use_io)
		printk( "using i/o access mode\n");

		nic->csr = pci_iomap(pdev, (use_io ? 1 : 0), sizeof(struct csr));
		/* Lionel 2008 map the control status register to virtual address for kernel */
		//nic->csr = ioremap(pci_resource_start(pdev,0),sizeof(struct csr));
		
		if(!nic->csr)
		{
			printk("can't map register to dma, exit!\n");
			ret = -ENOMEM;
			goto err_out_free_res;
		}
		if(pdi->driver_data)
			nic->flags |= ich;
		else
			nic->flags &= ~ich;
		ve100_get_defaults(nic);
		
		/* locks should be initialized */
		spin_lock_init(&nic->cb_lock);
		spin_lock_init(&nic->cmd_lock);
		spin_lock_init(&nic->mdio_lock);
		/* Reset the device before pci_set_master() in case device is in some
	 * funky state and has an interrupt pending - hint: we don't have the
	 * interrupt handler registered yet. */
	 ve100_hw_reset(nic);
	 pci_set_master(pdev);
	 /* Lionel 2008 Initialize the watchdog */
	 init_timer(&nic->watchdog);
	 nic->watchdog.function = ve100_watchdog;
	 nic->watchdog.data = (unsigned long)nic;
	 init_timer(&nic->blink_timer);
	 nic->blink_timer.function = ve100_blink_led;
	 nic->blink_timer.data = (unsigned long)nic;
	 
	 INIT_WORK(&nic->tx_timeout_task,ve100_tx_timeout_task);
	 /*Lionel 2008 alloc pci resources */
	if((ret = ve100_alloc(nic))) {
		printk("Cannot alloc driver memory, aborting.\n");
		goto err_out_iounmap;
	}
	if((ret = ve100_eeprom_load(nic)))
		goto err_out_free;
	ve100_phy_init(nic);
	
	memcpy(ndev->dev_addr,nic->eeprom,ETH_ALEN);
	memcpy(ndev->perm_addr,nic->eeprom,ETH_ALEN);
	if(!is_valid_ether_addr(ndev->perm_addr))
	{
		if(!eeprom_bad_csum_allow){
		printk("invalid address, exit!\n");
		ret = -EAGAIN;
		goto err_out_free;}
		else
			printk("Invalid Mac address from eeprom \n");
	}
	
/* I don't know whether the code is useful or not so I keep it */
	/* Wol magic packet can be enabled from eeprom */
	if((nic->mac >= mac_82558_D101_A4) &&
	   (nic->eeprom[eeprom_id] & eeprom_id_wol))
		nic->flags |= wol_magic;

	ret = pci_enable_wake(pdev,0,0);
	if(ret)
		printk("failed to wake\n");
	strcpy(ndev->name,"eth%d");
	
	/*Lionel 2008 register net device */
	if((ret = register_netdev(ndev)))
	{
			printk("can't register net device, exit!\n");
			goto err_out_free;
	}
	return 0;
err_out_free:
		ve100_free(nic);
err_out_iounmap:
		pci_iounmap(pdev,nic->csr);
err_out_free_res:
		pci_release_regions(pdev);
err_out_disable_pdev:
		pci_disable_device(pdev);
err_out_free_dev:
		pci_set_drvdata(pdev,NULL);
		free_netdev(ndev);
		return ret;
}
/* remove ve100 from system */
static void __devexit ve100_remove(struct pci_dev* pdev)
{
		struct net_device* ndev = pci_get_drvdata(pdev);
		if(ndev)
		{
				struct nic* nic = netdev_priv(ndev);
				unregister_netdev(ndev);           //unregister net device
				ve100_free(nic);                   //free net device descriptor*/
				iounmap(nic->csr);								 //unmap control status register*/
				free_netdev(ndev);								 //free net dev 
				pci_release_regions(pdev);         //release pci regions
				pci_disable_device(pdev);          //disable device
				pci_set_drvdata(pdev,NULL);        //set pdev as NULL
		}
}

/* shutdown */
static void ve100_shutdown(struct pci_dev* pdev)
{
		struct net_device* ndev = pci_get_drvdata(pdev);
		struct nic* nic = netdev_priv(ndev);
		if(netif_running(ndev))
			netif_poll_disable(nic->ndev);
		del_timer_sync(&nic->watchdog);
		netif_carrier_off(nic->ndev);
		
		if((nic->flags & wol_magic) | ve100_asf(nic))
		{
				pci_enable_wake(pdev,PCI_D3hot,1);
				pci_enable_wake(pdev,PCI_D3cold,1);
		}
		else
		{
				pci_enable_wake(pdev,PCI_D3hot,0);
				pci_enable_wake(pdev,PCI_D3cold,0);
		}
		pci_disable_device(pdev);
		pci_set_power_state(pdev,PCI_D3hot);
}

static struct pci_driver ve100_driver = {
	.name =         DRV_NAME,
	.id_table =     ve100_id_tbl,
	.probe =        ve100_probe,
	.remove =       __devexit_p(ve100_remove),

	.shutdown =     ve100_shutdown,
};

static int __init ve100_init_module(void)
{
	return pci_register_driver(&ve100_driver);
}

static void __exit ve100_cleanup_module(void)
{
		pci_unregister_driver(&ve100_driver);
}

module_init(ve100_init_module);
module_exit(ve100_cleanup_module);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -