pcnet32.c

来自「linux 内核源代码」· C语言 代码 · 共 2,361 行 · 第 1/5 页

C
2,361
字号
 * Any failure keeps old resources. * Must be called with lp->lock held. */static void pcnet32_realloc_tx_ring(struct net_device *dev,				    struct pcnet32_private *lp,				    unsigned int size){	dma_addr_t new_ring_dma_addr;	dma_addr_t *new_dma_addr_list;	struct pcnet32_tx_head *new_tx_ring;	struct sk_buff **new_skb_list;	pcnet32_purge_tx_ring(dev);	new_tx_ring = pci_alloc_consistent(lp->pci_dev,					   sizeof(struct pcnet32_tx_head) *					   (1 << size),					   &new_ring_dma_addr);	if (new_tx_ring == NULL) {		if (netif_msg_drv(lp))			printk("\n" KERN_ERR			       "%s: Consistent memory allocation failed.\n",			       dev->name);		return;	}	memset(new_tx_ring, 0, sizeof(struct pcnet32_tx_head) * (1 << size));	new_dma_addr_list = kcalloc((1 << size), sizeof(dma_addr_t),				GFP_ATOMIC);	if (!new_dma_addr_list) {		if (netif_msg_drv(lp))			printk("\n" KERN_ERR			       "%s: Memory allocation failed.\n", dev->name);		goto free_new_tx_ring;	}	new_skb_list = kcalloc((1 << size), sizeof(struct sk_buff *),				GFP_ATOMIC);	if (!new_skb_list) {		if (netif_msg_drv(lp))			printk("\n" KERN_ERR			       "%s: Memory allocation failed.\n", dev->name);		goto free_new_lists;	}	kfree(lp->tx_skbuff);	kfree(lp->tx_dma_addr);	pci_free_consistent(lp->pci_dev,			    sizeof(struct pcnet32_tx_head) *			    lp->tx_ring_size, lp->tx_ring,			    lp->tx_ring_dma_addr);	lp->tx_ring_size = (1 << size);	lp->tx_mod_mask = lp->tx_ring_size - 1;	lp->tx_len_bits = (size << 12);	lp->tx_ring = new_tx_ring;	lp->tx_ring_dma_addr = new_ring_dma_addr;	lp->tx_dma_addr = new_dma_addr_list;	lp->tx_skbuff = new_skb_list;	return;    free_new_lists:	kfree(new_dma_addr_list);    free_new_tx_ring:	pci_free_consistent(lp->pci_dev,			    sizeof(struct pcnet32_tx_head) *			    (1 << size),			    new_tx_ring,			    new_ring_dma_addr);	return;}/* * Allocate space for the new sized rx ring. * Re-use old receive buffers. *   alloc extra buffers *   free unneeded buffers *   free unneeded buffers * Save new resources. * Any failure keeps old resources. * Must be called with lp->lock held. */static void pcnet32_realloc_rx_ring(struct net_device *dev,				    struct pcnet32_private *lp,				    unsigned int size){	dma_addr_t new_ring_dma_addr;	dma_addr_t *new_dma_addr_list;	struct pcnet32_rx_head *new_rx_ring;	struct sk_buff **new_skb_list;	int new, overlap;	new_rx_ring = pci_alloc_consistent(lp->pci_dev,					   sizeof(struct pcnet32_rx_head) *					   (1 << size),					   &new_ring_dma_addr);	if (new_rx_ring == NULL) {		if (netif_msg_drv(lp))			printk("\n" KERN_ERR			       "%s: Consistent memory allocation failed.\n",			       dev->name);		return;	}	memset(new_rx_ring, 0, sizeof(struct pcnet32_rx_head) * (1 << size));	new_dma_addr_list = kcalloc((1 << size), sizeof(dma_addr_t),				GFP_ATOMIC);	if (!new_dma_addr_list) {		if (netif_msg_drv(lp))			printk("\n" KERN_ERR			       "%s: Memory allocation failed.\n", dev->name);		goto free_new_rx_ring;	}	new_skb_list = kcalloc((1 << size), sizeof(struct sk_buff *),				GFP_ATOMIC);	if (!new_skb_list) {		if (netif_msg_drv(lp))			printk("\n" KERN_ERR			       "%s: Memory allocation failed.\n", dev->name);		goto free_new_lists;	}	/* first copy the current receive buffers */	overlap = min(size, lp->rx_ring_size);	for (new = 0; new < overlap; new++) {		new_rx_ring[new] = lp->rx_ring[new];		new_dma_addr_list[new] = lp->rx_dma_addr[new];		new_skb_list[new] = lp->rx_skbuff[new];	}	/* now allocate any new buffers needed */	for (; new < size; new++ ) {		struct sk_buff *rx_skbuff;		new_skb_list[new] = dev_alloc_skb(PKT_BUF_SZ);		if (!(rx_skbuff = new_skb_list[new])) {			/* keep the original lists and buffers */			if (netif_msg_drv(lp))				printk(KERN_ERR				       "%s: pcnet32_realloc_rx_ring dev_alloc_skb failed.\n",				       dev->name);			goto free_all_new;		}		skb_reserve(rx_skbuff, 2);		new_dma_addr_list[new] =			    pci_map_single(lp->pci_dev, rx_skbuff->data,					   PKT_BUF_SZ - 2, PCI_DMA_FROMDEVICE);		new_rx_ring[new].base = cpu_to_le32(new_dma_addr_list[new]);		new_rx_ring[new].buf_length = cpu_to_le16(2 - PKT_BUF_SZ);		new_rx_ring[new].status = cpu_to_le16(0x8000);	}	/* and free any unneeded buffers */	for (; new < lp->rx_ring_size; new++) {		if (lp->rx_skbuff[new]) {			pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[new],					 PKT_BUF_SZ - 2, PCI_DMA_FROMDEVICE);			dev_kfree_skb(lp->rx_skbuff[new]);		}	}	kfree(lp->rx_skbuff);	kfree(lp->rx_dma_addr);	pci_free_consistent(lp->pci_dev,			    sizeof(struct pcnet32_rx_head) *			    lp->rx_ring_size, lp->rx_ring,			    lp->rx_ring_dma_addr);	lp->rx_ring_size = (1 << size);	lp->rx_mod_mask = lp->rx_ring_size - 1;	lp->rx_len_bits = (size << 4);	lp->rx_ring = new_rx_ring;	lp->rx_ring_dma_addr = new_ring_dma_addr;	lp->rx_dma_addr = new_dma_addr_list;	lp->rx_skbuff = new_skb_list;	return;    free_all_new:	for (; --new >= lp->rx_ring_size; ) {		if (new_skb_list[new]) {			pci_unmap_single(lp->pci_dev, new_dma_addr_list[new],					 PKT_BUF_SZ - 2, PCI_DMA_FROMDEVICE);			dev_kfree_skb(new_skb_list[new]);		}	}	kfree(new_skb_list);    free_new_lists:	kfree(new_dma_addr_list);    free_new_rx_ring:	pci_free_consistent(lp->pci_dev,			    sizeof(struct pcnet32_rx_head) *			    (1 << size),			    new_rx_ring,			    new_ring_dma_addr);	return;}static void pcnet32_purge_rx_ring(struct net_device *dev){	struct pcnet32_private *lp = netdev_priv(dev);	int i;	/* free all allocated skbuffs */	for (i = 0; i < lp->rx_ring_size; i++) {		lp->rx_ring[i].status = 0;	/* CPU owns buffer */		wmb();		/* Make sure adapter sees owner change */		if (lp->rx_skbuff[i]) {			pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i],					 PKT_BUF_SZ - 2, PCI_DMA_FROMDEVICE);			dev_kfree_skb_any(lp->rx_skbuff[i]);		}		lp->rx_skbuff[i] = NULL;		lp->rx_dma_addr[i] = 0;	}}#ifdef CONFIG_NET_POLL_CONTROLLERstatic void pcnet32_poll_controller(struct net_device *dev){	disable_irq(dev->irq);	pcnet32_interrupt(0, dev);	enable_irq(dev->irq);}#endifstatic int pcnet32_get_settings(struct net_device *dev, struct ethtool_cmd *cmd){	struct pcnet32_private *lp = netdev_priv(dev);	unsigned long flags;	int r = -EOPNOTSUPP;	if (lp->mii) {		spin_lock_irqsave(&lp->lock, flags);		mii_ethtool_gset(&lp->mii_if, cmd);		spin_unlock_irqrestore(&lp->lock, flags);		r = 0;	}	return r;}static int pcnet32_set_settings(struct net_device *dev, struct ethtool_cmd *cmd){	struct pcnet32_private *lp = netdev_priv(dev);	unsigned long flags;	int r = -EOPNOTSUPP;	if (lp->mii) {		spin_lock_irqsave(&lp->lock, flags);		r = mii_ethtool_sset(&lp->mii_if, cmd);		spin_unlock_irqrestore(&lp->lock, flags);	}	return r;}static void pcnet32_get_drvinfo(struct net_device *dev,				struct ethtool_drvinfo *info){	struct pcnet32_private *lp = netdev_priv(dev);	strcpy(info->driver, DRV_NAME);	strcpy(info->version, DRV_VERSION);	if (lp->pci_dev)		strcpy(info->bus_info, pci_name(lp->pci_dev));	else		sprintf(info->bus_info, "VLB 0x%lx", dev->base_addr);}static u32 pcnet32_get_link(struct net_device *dev){	struct pcnet32_private *lp = netdev_priv(dev);	unsigned long flags;	int r;	spin_lock_irqsave(&lp->lock, flags);	if (lp->mii) {		r = mii_link_ok(&lp->mii_if);	} else if (lp->chip_version >= PCNET32_79C970A) {		ulong ioaddr = dev->base_addr;	/* card base I/O address */		r = (lp->a.read_bcr(ioaddr, 4) != 0xc0);	} else {	/* can not detect link on really old chips */		r = 1;	}	spin_unlock_irqrestore(&lp->lock, flags);	return r;}static u32 pcnet32_get_msglevel(struct net_device *dev){	struct pcnet32_private *lp = netdev_priv(dev);	return lp->msg_enable;}static void pcnet32_set_msglevel(struct net_device *dev, u32 value){	struct pcnet32_private *lp = netdev_priv(dev);	lp->msg_enable = value;}static int pcnet32_nway_reset(struct net_device *dev){	struct pcnet32_private *lp = netdev_priv(dev);	unsigned long flags;	int r = -EOPNOTSUPP;	if (lp->mii) {		spin_lock_irqsave(&lp->lock, flags);		r = mii_nway_restart(&lp->mii_if);		spin_unlock_irqrestore(&lp->lock, flags);	}	return r;}static void pcnet32_get_ringparam(struct net_device *dev,				  struct ethtool_ringparam *ering){	struct pcnet32_private *lp = netdev_priv(dev);	ering->tx_max_pending = TX_MAX_RING_SIZE;	ering->tx_pending = lp->tx_ring_size;	ering->rx_max_pending = RX_MAX_RING_SIZE;	ering->rx_pending = lp->rx_ring_size;}static int pcnet32_set_ringparam(struct net_device *dev,				 struct ethtool_ringparam *ering){	struct pcnet32_private *lp = netdev_priv(dev);	unsigned long flags;	unsigned int size;	ulong ioaddr = dev->base_addr;	int i;	if (ering->rx_mini_pending || ering->rx_jumbo_pending)		return -EINVAL;	if (netif_running(dev))		pcnet32_netif_stop(dev);	spin_lock_irqsave(&lp->lock, flags);	lp->a.write_csr(ioaddr, CSR0, CSR0_STOP);	/* stop the chip */	size = min(ering->tx_pending, (unsigned int)TX_MAX_RING_SIZE);	/* set the minimum ring size to 4, to allow the loopback test to work	 * unchanged.	 */	for (i = 2; i <= PCNET32_LOG_MAX_TX_BUFFERS; i++) {		if (size <= (1 << i))			break;	}	if ((1 << i) != lp->tx_ring_size)		pcnet32_realloc_tx_ring(dev, lp, i);	size = min(ering->rx_pending, (unsigned int)RX_MAX_RING_SIZE);	for (i = 2; i <= PCNET32_LOG_MAX_RX_BUFFERS; i++) {		if (size <= (1 << i))			break;	}	if ((1 << i) != lp->rx_ring_size)		pcnet32_realloc_rx_ring(dev, lp, i);	lp->napi.weight = lp->rx_ring_size / 2;	if (netif_running(dev)) {		pcnet32_netif_start(dev);		pcnet32_restart(dev, CSR0_NORMAL);	}	spin_unlock_irqrestore(&lp->lock, flags);	if (netif_msg_drv(lp))		printk(KERN_INFO		       "%s: Ring Param Settings: RX: %d, TX: %d\n", dev->name,		       lp->rx_ring_size, lp->tx_ring_size);	return 0;}static void pcnet32_get_strings(struct net_device *dev, u32 stringset,				u8 * data){	memcpy(data, pcnet32_gstrings_test, sizeof(pcnet32_gstrings_test));}static int pcnet32_get_sset_count(struct net_device *dev, int sset){	switch (sset) {	case ETH_SS_TEST:		return PCNET32_TEST_LEN;	default:		return -EOPNOTSUPP;	}}static void pcnet32_ethtool_test(struct net_device *dev,				 struct ethtool_test *test, u64 * data){	struct pcnet32_private *lp = netdev_priv(dev);	int rc;	if (test->flags == ETH_TEST_FL_OFFLINE) {		rc = pcnet32_loopback_test(dev, data);		if (rc) {			if (netif_msg_hw(lp))				printk(KERN_DEBUG "%s: Loopback test failed.\n",				       dev->name);			test->flags |= ETH_TEST_FL_FAILED;		} else if (netif_msg_hw(lp))			printk(KERN_DEBUG "%s: Loopback test passed.\n",			       dev->name);	} else if (netif_msg_hw(lp))		printk(KERN_DEBUG		       "%s: No tests to run (specify 'Offline' on ethtool).",		       dev->name);}				/* end pcnet32_ethtool_test */static int pcnet32_loopback_test(struct net_device *dev, uint64_t * data1){	struct pcnet32_private *lp = netdev_priv(dev);	struct pcnet32_access *a = &lp->a;	/* access to registers */	ulong ioaddr = dev->base_addr;	/* card base I/O address */	struct sk_buff *skb;	/* sk buff */	int x, i;		/* counters */	int numbuffs = 4;	/* number of TX/RX buffers and descs */	u16 status = 0x8300;	/* TX ring status */	__le16 teststatus;	/* test of ring status */	int rc;			/* return code */	int size;		/* size of packets */	unsigned char *packet;	/* source packet data */	static const int data_len = 60;	/* length of source packets */	unsigned long flags;	unsigned long ticks;	rc = 1;			/* default to fail */	if (netif_running(dev))#ifdef CONFIG_PCNET32_NAPI		pcnet32_netif_stop(dev);#else		pcnet32_close(dev);#endif	spin_lock_irqsave(&lp->lock, flags);	lp->a.write_csr(ioaddr, CSR0, CSR0_STOP);	/* stop the chip */	numbuffs = min(numbuffs, (int)min(lp->rx_ring_size, lp->tx_ring_size));	/* Reset the PCNET32 */	lp->a.reset(ioaddr);	lp->a.write_csr(ioaddr, CSR4, 0x0915);	/* auto tx pad */	/* switch pcnet32 to 32bit mode */	lp->a.write_bcr(ioaddr, 20, 2);	/* purge & init rings but don't actually restart */	pcnet32_restart(dev, 0x0000);	lp->a.write_csr(ioaddr, CSR0, CSR0_STOP);	/* Set STOP bit */	/* Initialize Transmit buffers. */	size = data_len + 15;	for (x = 0; x < numbuffs; x++) {		if (!(skb = dev_alloc_skb(size))) {			if (netif_msg_hw(lp))				printk(KERN_DEBUG				       "%s: Cannot allocate skb at line: %d!\n",				       dev->name, __LINE__);			goto clean_up;		} else {			packet = skb->data;			skb_put(skb, size);	/* create space for data */			lp->tx_skbuff[x] = skb;			lp->tx_ring[x].length = cpu_to_le16(-skb->len);

⌨️ 快捷键说明

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