3c515.c

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

C
1,591
字号
/* Note: this is the only limit on the number of cards supported!! */static int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1, };#ifdef MODULEstatic int debug = -1;module_param(debug, int, 0);module_param_array(options, int, NULL, 0);module_param(rx_copybreak, int, 0);module_param(max_interrupt_work, int, 0);MODULE_PARM_DESC(debug, "3c515 debug level (0-6)");MODULE_PARM_DESC(options, "3c515: Bits 0-2: media type, bit 3: full duplex, bit 4: bus mastering");MODULE_PARM_DESC(rx_copybreak, "3c515 copy breakpoint for copy-only-tiny-frames");MODULE_PARM_DESC(max_interrupt_work, "3c515 maximum events handled per interrupt");/* A list of all installed Vortex devices, for removing the driver module. *//* we will need locking (and refcounting) if we ever use it for more */static LIST_HEAD(root_corkscrew_dev);int init_module(void){	int found = 0;	if (debug >= 0)		corkscrew_debug = debug;	if (corkscrew_debug)		printk(version);	while (corkscrew_scan(-1))		found++;	return found ? 0 : -ENODEV;}#elsestruct net_device *tc515_probe(int unit){	struct net_device *dev = corkscrew_scan(unit);	static int printed;	if (!dev)		return ERR_PTR(-ENODEV);	if (corkscrew_debug > 0 && !printed) {		printed = 1;		printk(version);	}	return dev;}#endif				/* not MODULE */static int check_device(unsigned ioaddr){	int timer;	if (!request_region(ioaddr, CORKSCREW_TOTAL_SIZE, "3c515"))		return 0;	/* Check the resource configuration for a matching ioaddr. */	if ((inw(ioaddr + 0x2002) & 0x1f0) != (ioaddr & 0x1f0)) {		release_region(ioaddr, CORKSCREW_TOTAL_SIZE);		return 0;	}	/* Verify by reading the device ID from the EEPROM. */	outw(EEPROM_Read + 7, ioaddr + Wn0EepromCmd);	/* Pause for at least 162 us. for the read to take place. */	for (timer = 4; timer >= 0; timer--) {		udelay(162);		if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0)			break;	}	if (inw(ioaddr + Wn0EepromData) != 0x6d50) {		release_region(ioaddr, CORKSCREW_TOTAL_SIZE);		return 0;	}	return 1;}static void cleanup_card(struct net_device *dev){	struct corkscrew_private *vp = netdev_priv(dev);	list_del_init(&vp->list);	if (dev->dma)		free_dma(dev->dma);	outw(TotalReset, dev->base_addr + EL3_CMD);	release_region(dev->base_addr, CORKSCREW_TOTAL_SIZE);	if (vp->dev)		pnp_device_detach(to_pnp_dev(vp->dev));}static struct net_device *corkscrew_scan(int unit){	struct net_device *dev;	static int cards_found = 0;	static int ioaddr;	int err;#ifdef __ISAPNP__	short i;	static int pnp_cards;#endif	dev = alloc_etherdev(sizeof(struct corkscrew_private));	if (!dev)		return ERR_PTR(-ENOMEM);	if (unit >= 0) {		sprintf(dev->name, "eth%d", unit);		netdev_boot_setup_check(dev);	}#ifdef __ISAPNP__	if(nopnp == 1)		goto no_pnp;	for(i=0; corkscrew_isapnp_adapters[i].vendor != 0; i++) {		struct pnp_dev *idev = NULL;		int irq;		while((idev = pnp_find_dev(NULL,					   corkscrew_isapnp_adapters[i].vendor,					   corkscrew_isapnp_adapters[i].function,					   idev))) {			if (pnp_device_attach(idev) < 0)				continue;			if (pnp_activate_dev(idev) < 0) {				printk("pnp activate failed (out of resources?)\n");				pnp_device_detach(idev);				continue;			}			if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0)) {				pnp_device_detach(idev);				continue;			}			ioaddr = pnp_port_start(idev, 0);			irq = pnp_irq(idev, 0);			if (!check_device(ioaddr)) {				pnp_device_detach(idev);				continue;			}			if(corkscrew_debug)				printk ("ISAPNP reports %s at i/o 0x%x, irq %d\n",					(char*) corkscrew_isapnp_adapters[i].driver_data, ioaddr, irq);			printk(KERN_INFO "3c515 Resource configuration register %#4.4x, DCR %4.4x.\n",		     		inl(ioaddr + 0x2002), inw(ioaddr + 0x2000));			/* irq = inw(ioaddr + 0x2002) & 15; */ /* Use the irq from isapnp */			SET_NETDEV_DEV(dev, &idev->dev);			pnp_cards++;			err = corkscrew_setup(dev, ioaddr, idev, cards_found++);			if (!err)				return dev;			cleanup_card(dev);		}	}no_pnp:#endif /* __ISAPNP__ */	/* Check all locations on the ISA bus -- evil! */	for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20) {		if (!check_device(ioaddr))			continue;		printk(KERN_INFO "3c515 Resource configuration register %#4.4x, DCR %4.4x.\n",		     inl(ioaddr + 0x2002), inw(ioaddr + 0x2000));		err = corkscrew_setup(dev, ioaddr, NULL, cards_found++);		if (!err)			return dev;		cleanup_card(dev);	}	free_netdev(dev);	return NULL;}static int corkscrew_setup(struct net_device *dev, int ioaddr,			    struct pnp_dev *idev, int card_number){	struct corkscrew_private *vp = netdev_priv(dev);	unsigned int eeprom[0x40], checksum = 0;	/* EEPROM contents */	int i;	int irq;	DECLARE_MAC_BUF(mac);	if (idev) {		irq = pnp_irq(idev, 0);		vp->dev = &idev->dev;	} else {		irq = inw(ioaddr + 0x2002) & 15;	}	dev->base_addr = ioaddr;	dev->irq = irq;	dev->dma = inw(ioaddr + 0x2000) & 7;	vp->product_name = "3c515";	vp->options = dev->mem_start;	vp->our_dev = dev;	if (!vp->options) {		 if (card_number >= MAX_UNITS)			vp->options = -1;		else			vp->options = options[card_number];	}	if (vp->options >= 0) {		vp->media_override = vp->options & 7;		if (vp->media_override == 2)			vp->media_override = 0;		vp->full_duplex = (vp->options & 8) ? 1 : 0;		vp->bus_master = (vp->options & 16) ? 1 : 0;	} else {		vp->media_override = 7;		vp->full_duplex = 0;		vp->bus_master = 0;	}#ifdef MODULE	list_add(&vp->list, &root_corkscrew_dev);#endif	printk(KERN_INFO "%s: 3Com %s at %#3x,", dev->name, vp->product_name, ioaddr);	spin_lock_init(&vp->lock);	/* Read the station address from the EEPROM. */	EL3WINDOW(0);	for (i = 0; i < 0x18; i++) {		__be16 *phys_addr = (__be16 *) dev->dev_addr;		int timer;		outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd);		/* Pause for at least 162 us. for the read to take place. */		for (timer = 4; timer >= 0; timer--) {			udelay(162);			if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0)				break;		}		eeprom[i] = inw(ioaddr + Wn0EepromData);		checksum ^= eeprom[i];		if (i < 3)			phys_addr[i] = htons(eeprom[i]);	}	checksum = (checksum ^ (checksum >> 8)) & 0xff;	if (checksum != 0x00)		printk(" ***INVALID CHECKSUM %4.4x*** ", checksum);	printk(" %s", print_mac(mac, dev->dev_addr));	if (eeprom[16] == 0x11c7) {	/* Corkscrew */		if (request_dma(dev->dma, "3c515")) {			printk(", DMA %d allocation failed", dev->dma);			dev->dma = 0;		} else			printk(", DMA %d", dev->dma);	}	printk(", IRQ %d\n", dev->irq);	/* Tell them about an invalid IRQ. */	if (corkscrew_debug && (dev->irq <= 0 || dev->irq > 15))		printk(KERN_WARNING " *** Warning: this IRQ is unlikely to work! ***\n");	{		char *ram_split[] = { "5:3", "3:1", "1:1", "3:5" };		__u32 config;		EL3WINDOW(3);		vp->available_media = inw(ioaddr + Wn3_Options);		config = inl(ioaddr + Wn3_Config);		if (corkscrew_debug > 1)			printk(KERN_INFO "  Internal config register is %4.4x, transceivers %#x.\n",				config, inw(ioaddr + Wn3_Options));		printk(KERN_INFO "  %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n",			8 << config & Ram_size,			config & Ram_width ? "word" : "byte",			ram_split[(config & Ram_split) >> Ram_split_shift],			config & Autoselect ? "autoselect/" : "",			media_tbl[(config & Xcvr) >> Xcvr_shift].name);		vp->default_media = (config & Xcvr) >> Xcvr_shift;		vp->autoselect = config & Autoselect ? 1 : 0;		dev->if_port = vp->default_media;	}	if (vp->media_override != 7) {		printk(KERN_INFO "  Media override to transceiver type %d (%s).\n",		       vp->media_override,		       media_tbl[vp->media_override].name);		dev->if_port = vp->media_override;	}	vp->capabilities = eeprom[16];	vp->full_bus_master_tx = (vp->capabilities & 0x20) ? 1 : 0;	/* Rx is broken at 10mbps, so we always disable it. */	/* vp->full_bus_master_rx = 0; */	vp->full_bus_master_rx = (vp->capabilities & 0x20) ? 1 : 0;	/* The 3c51x-specific entries in the device structure. */	dev->open = &corkscrew_open;	dev->hard_start_xmit = &corkscrew_start_xmit;	dev->tx_timeout = &corkscrew_timeout;	dev->watchdog_timeo = (400 * HZ) / 1000;	dev->stop = &corkscrew_close;	dev->get_stats = &corkscrew_get_stats;	dev->set_multicast_list = &set_rx_mode;	dev->ethtool_ops = &netdev_ethtool_ops;	return register_netdev(dev);}static int corkscrew_open(struct net_device *dev){	int ioaddr = dev->base_addr;	struct corkscrew_private *vp = netdev_priv(dev);	__u32 config;	int i;	/* Before initializing select the active media port. */	EL3WINDOW(3);	if (vp->full_duplex)		outb(0x20, ioaddr + Wn3_MAC_Ctrl);	/* Set the full-duplex bit. */	config = inl(ioaddr + Wn3_Config);	if (vp->media_override != 7) {		if (corkscrew_debug > 1)			printk(KERN_INFO "%s: Media override to transceiver %d (%s).\n",				dev->name, vp->media_override,				media_tbl[vp->media_override].name);		dev->if_port = vp->media_override;	} else if (vp->autoselect) {		/* Find first available media type, starting with 100baseTx. */		dev->if_port = 4;		while (!(vp->available_media & media_tbl[dev->if_port].mask))			dev->if_port = media_tbl[dev->if_port].next;		if (corkscrew_debug > 1)			printk("%s: Initial media type %s.\n",			       dev->name, media_tbl[dev->if_port].name);		init_timer(&vp->timer);		vp->timer.expires = jiffies + media_tbl[dev->if_port].wait;		vp->timer.data = (unsigned long) dev;		vp->timer.function = &corkscrew_timer;	/* timer handler */		add_timer(&vp->timer);	} else		dev->if_port = vp->default_media;	config = (config & ~Xcvr) | (dev->if_port << Xcvr_shift);	outl(config, ioaddr + Wn3_Config);	if (corkscrew_debug > 1) {		printk("%s: corkscrew_open() InternalConfig %8.8x.\n",		       dev->name, config);	}	outw(TxReset, ioaddr + EL3_CMD);	for (i = 20; i >= 0; i--)		if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))			break;	outw(RxReset, ioaddr + EL3_CMD);	/* Wait a few ticks for the RxReset command to complete. */	for (i = 20; i >= 0; i--)		if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))			break;	outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);	/* Use the now-standard shared IRQ implementation. */	if (vp->capabilities == 0x11c7) {		/* Corkscrew: Cannot share ISA resources. */		if (dev->irq == 0		    || dev->dma == 0		    || request_irq(dev->irq, &corkscrew_interrupt, 0,				   vp->product_name, dev)) return -EAGAIN;		enable_dma(dev->dma);		set_dma_mode(dev->dma, DMA_MODE_CASCADE);	} else if (request_irq(dev->irq, &corkscrew_interrupt, IRQF_SHARED,			       vp->product_name, dev)) {		return -EAGAIN;	}	if (corkscrew_debug > 1) {		EL3WINDOW(4);		printk("%s: corkscrew_open() irq %d media status %4.4x.\n",		       dev->name, dev->irq, inw(ioaddr + Wn4_Media));	}	/* Set the station address and mask in window 2 each time opened. */	EL3WINDOW(2);	for (i = 0; i < 6; i++)		outb(dev->dev_addr[i], ioaddr + i);	for (; i < 12; i += 2)		outw(0, ioaddr + i);	if (dev->if_port == 3)		/* Start the thinnet transceiver. We should really wait 50ms... */		outw(StartCoax, ioaddr + EL3_CMD);	EL3WINDOW(4);	outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP | Media_SQE)) |	     media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media);	/* Switch to the stats window, and clear all stats by reading. */	outw(StatsDisable, ioaddr + EL3_CMD);	EL3WINDOW(6);	for (i = 0; i < 10; i++)		inb(ioaddr + i);	inw(ioaddr + 10);	inw(ioaddr + 12);	/* New: On the Vortex we must also clear the BadSSD counter. */	EL3WINDOW(4);	inb(ioaddr + 12);

⌨️ 快捷键说明

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