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

📄 pegasus.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/***	Pegasus: USB 10/100Mbps/HomePNA (1Mbps) Controller****	Copyright (c) 1999,2000 Petko Manolov - Petkan (petkan@dce.bg)**	****	ChangeLog:**		....	Most of the time spend reading sources & docs.**		v0.2.x	First official release for the Linux kernel.**		v0.3.0	Beutified and structured, some bugs fixed.**		v0.3.x	URBifying bulk requests and bugfixing. First relatively**			stable release. Still can touch device's registers only**			from top-halves.**		v0.4.0	Control messages remained unurbified are now URBs.**			Now we can touch the HW at any time.**		v0.4.9	Control urbs again use process context to wait. Argh...**			Some long standing bugs (enable_net_traffic) fixed.**			Also nasty trick about resubmiting control urb from**			interrupt context used. Please let me know how it**			behaves. Pegasus II support added since this version.**			TODO: suppressing HCD warnings spewage on disconnect.**		v0.4.13	Ethernet address is now set at probe(), not at open()**			time as this seems to break dhcpd. *//* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <linux/sched.h>#include <linux/malloc.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/usb.h>#include <linux/module.h>#include "pegasus.h"#define	PEGASUS_USE_INTR#define	PEGASUS_WRITE_EEPROMstatic const char *version = __FILE__ ": v0.4.17 2000/11/13 (C) 1999-2000 Petko Manolov (petkan@dce.bg)";static int loopback = 0;static int mii_mode = 0;static int multicast_filter_limit = 32;static struct usb_eth_dev usb_dev_id[] = {#define	PEGASUS_DEV(pn, vid, pid, flags)	\	{name:pn, vendor:vid, device:pid, private:flags},#include "pegasus.h"#undef	PEGASUS_DEV	{NULL, 0, 0, 0}};static struct usb_device_id pegasus_ids[] = {#define	PEGASUS_DEV(pn, vid, pid, flags) \	{match_flags: USB_DEVICE_ID_MATCH_DEVICE, idVendor:vid, idProduct:pid},#include "pegasus.h"#undef	PEGASUS_DEV	{ }};MODULE_AUTHOR("Petko Manolov <petkan@dce.bg>");MODULE_DESCRIPTION("ADMtek AN986 Pegasus USB Ethernet driver");MODULE_PARM(loopback, "i");MODULE_PARM(mii_mode, "i");MODULE_PARM_DESC(loopback, "Enable MAC loopback mode (bit 0)");MODULE_PARM_DESC(mii_mode, "Enable HomePNA mode (bit 0),default=MII mode = 0");MODULE_DEVICE_TABLE (usb, pegasus_ids);static int update_eth_regs_async( pegasus_t * );/* Aargh!!! I _really_ hate such tweaks */static void ctrl_callback( urb_t *urb ){	pegasus_t	*pegasus = urb->context;	if ( !pegasus )		return;	switch ( urb->status ) {		case USB_ST_NOERROR:			if ( pegasus->flags & ETH_REGS_CHANGE ) {				pegasus->flags &= ~ETH_REGS_CHANGE;				pegasus->flags |= ETH_REGS_CHANGED;				update_eth_regs_async( pegasus );				return;			}			break;		case USB_ST_URB_PENDING:			return;		case USB_ST_URB_KILLED:			break;		default:			warn( __FUNCTION__ " status %d", urb->status);	}	pegasus->flags &= ~ETH_REGS_CHANGED;	if ( pegasus->flags & CTRL_URB_SLEEP ) {		pegasus->flags &= ~CTRL_URB_SLEEP;		wake_up_interruptible( &pegasus->ctrl_wait );	}}static int get_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data){	int	ret;	while ( pegasus->flags & ETH_REGS_CHANGED ) {		pegasus->flags |= CTRL_URB_SLEEP;		interruptible_sleep_on( &pegasus->ctrl_wait );	}	pegasus->dr.requesttype = PEGASUS_REQT_READ;	pegasus->dr.request = PEGASUS_REQ_GET_REGS;	pegasus->dr.value = 0;	pegasus->dr.index = cpu_to_le16p(&indx);	pegasus->dr.length = 	pegasus->ctrl_urb.transfer_buffer_length = cpu_to_le16p(&size);	FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb,			  usb_rcvctrlpipe(pegasus->usb,0),			  (char *)&pegasus->dr,			  data, size, ctrl_callback, pegasus );	if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) {		err( __FUNCTION__ " BAD CTRLs %d", ret);		goto out;	}	pegasus->flags |= CTRL_URB_SLEEP;	interruptible_sleep_on( &pegasus->ctrl_wait );out:	return	ret;}static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data){	int	ret;	while ( pegasus->flags & ETH_REGS_CHANGED ) {		pegasus->flags |= CTRL_URB_SLEEP ;		interruptible_sleep_on( &pegasus->ctrl_wait );	}	pegasus->dr.requesttype = PEGASUS_REQT_WRITE;	pegasus->dr.request = PEGASUS_REQ_SET_REGS;	pegasus->dr.value = 0;	pegasus->dr.index = cpu_to_le16p( &indx );	pegasus->dr.length = 	pegasus->ctrl_urb.transfer_buffer_length = cpu_to_le16p( &size );	FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb,			  usb_sndctrlpipe(pegasus->usb,0),			  (char *)&pegasus->dr,			  data, size, ctrl_callback, pegasus );	if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) {		err( __FUNCTION__ " BAD CTRL %d", ret);		return	ret;	}	pegasus->flags |= CTRL_URB_SLEEP;	interruptible_sleep_on( &pegasus->ctrl_wait );	return	ret;}static int set_register( pegasus_t *pegasus, __u16 indx, __u8 data ){	int	ret;	while ( pegasus->flags & ETH_REGS_CHANGED ) {		pegasus->flags |= CTRL_URB_SLEEP;		interruptible_sleep_on( &pegasus->ctrl_wait );	}	pegasus->dr.requesttype = PEGASUS_REQT_WRITE;	pegasus->dr.request = PEGASUS_REQ_SET_REG;	pegasus->dr.value = data;	pegasus->dr.index = cpu_to_le16p( &indx );	pegasus->dr.length = pegasus->ctrl_urb.transfer_buffer_length = 1;	FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb,			  usb_sndctrlpipe(pegasus->usb,0),			  (char *)&pegasus->dr,			  &data, 1, ctrl_callback, pegasus );	if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) {		err( __FUNCTION__ " BAD CTRL %d", ret);		return	ret;	}	pegasus->flags |= CTRL_URB_SLEEP;	interruptible_sleep_on( &pegasus->ctrl_wait );		return	ret;}static int update_eth_regs_async( pegasus_t *pegasus ){	int	ret;	pegasus->dr.requesttype = PEGASUS_REQT_WRITE;	pegasus->dr.request = PEGASUS_REQ_SET_REGS;	pegasus->dr.value = 0;	pegasus->dr.index = EthCtrl0;	pegasus->dr.length = 	pegasus->ctrl_urb.transfer_buffer_length = 3;	FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb,			  usb_sndctrlpipe(pegasus->usb,0),			  (char *)&pegasus->dr,			  pegasus->eth_regs, 3, ctrl_callback, pegasus );	if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) )		err( __FUNCTION__ " BAD CTRL %d, flags %x",ret,pegasus->flags );	return	ret;}static int read_mii_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd ){	int	i;	__u8	data[4] = { phy, 0, 0, indx };	set_register( pegasus, PhyCtrl, 0 );	set_registers( pegasus, PhyAddr, sizeof(data), data );	set_register( pegasus, PhyCtrl, (indx | PHY_READ) );	for (i = 0; i < REG_TIMEOUT; i++) {		get_registers(pegasus, PhyCtrl, 1, data);		if ( data[0] & PHY_DONE ) 			break;	}	if ( i < REG_TIMEOUT ) {		get_registers( pegasus, PhyData, 2, regd );		return	0;	}	warn( __FUNCTION__ " failed" );		return 1;}static int write_mii_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 regd ){	int	i;	__u8	data[4] = { phy, 0, 0, indx };		*(data + 1) = cpu_to_le16p( &regd );	set_register( pegasus, PhyCtrl, 0 );	set_registers( pegasus, PhyAddr, 4, data );	set_register( pegasus, PhyCtrl, (indx | PHY_WRITE) );	for (i = 0; i < REG_TIMEOUT; i++) {		get_registers(pegasus, PhyCtrl, 1, data);		if ( data[0] & PHY_DONE ) 			break;	}	if ( i < REG_TIMEOUT )		return	0;	warn( __FUNCTION__ " failed" );	return 1;}static int read_eprom_word( pegasus_t *pegasus, __u8 index, __u16 *retdata ){	int	i, tmp;	set_register( pegasus, EpromCtrl, 0 );	set_register( pegasus, EpromOffset, index );	set_register( pegasus, EpromCtrl, EPROM_READ); 	for ( i=0; i < REG_TIMEOUT; i++ ) {		get_registers( pegasus, EpromCtrl, 1, &tmp );		if ( tmp & EPROM_DONE )			break;	}	if ( i < REG_TIMEOUT ) {		get_registers( pegasus, EpromData, 2, retdata );		return	0;	}	warn( __FUNCTION__ " failed" );	return -1;}#ifdef	PEGASUS_WRITE_EEPROMstatic inline void enable_eprom_write( pegasus_t *pegasus ){	__u8	tmp;	get_registers( pegasus, EthCtrl2, 1, &tmp );	set_register( pegasus, EthCtrl2, tmp | EPROM_WR_ENABLE );}static inline void disable_eprom_write( pegasus_t *pegasus ){	__u8 	tmp;	get_registers( pegasus, EthCtrl2, 1, &tmp );	set_register( pegasus, EpromCtrl, 0 );	set_register( pegasus, EthCtrl2, tmp & ~EPROM_WR_ENABLE );}static int write_eprom_word( pegasus_t *pegasus, __u8 index, __u16 data ){	int	i, tmp;	__u8	d[4] = {0x3f, 0, 0, EPROM_WRITE};	set_registers( pegasus, EpromOffset, 4, d );	enable_eprom_write( pegasus );	set_register( pegasus, EpromOffset, index );	set_registers( pegasus, EpromData, 2, &data );	set_register( pegasus, EpromCtrl, EPROM_WRITE );	for ( i=0; i < REG_TIMEOUT; i++ ) {		get_registers( pegasus, EpromCtrl, 1, &tmp );		if ( tmp & EPROM_DONE )			break;	}	disable_eprom_write( pegasus );	if ( i < REG_TIMEOUT )		return	0;	warn( __FUNCTION__ " failed" );	return	-1;}#endif	/* PEGASUS_WRITE_EEPROM */static inline void get_node_id( pegasus_t *pegasus, __u8 *id ){	int	i;	for (i = 0; i < 3; i++)		read_eprom_word( pegasus, i, (__u16 *)&id[i*2]);}static void set_ethernet_addr( pegasus_t *pegasus ){	__u8	node_id[6];	get_node_id(pegasus, node_id);	set_registers( pegasus, EthID, sizeof(node_id), node_id );	memcpy( pegasus->net->dev_addr, node_id, sizeof(node_id) );}static inline int reset_mac( pegasus_t *pegasus ){	__u8	data = 0x8;	int	i;	set_register(pegasus, EthCtrl1, data);	for (i = 0; i < REG_TIMEOUT; i++) {		get_registers(pegasus, EthCtrl1, 1, &data);		if (~data & 0x08) {			if (loopback & 1) 				break;			if ( mii_mode && (pegasus->features & HAS_HOME_PNA) )				set_register( pegasus, Gpio1, 0x34 );			else				set_register( pegasus, Gpio1, 0x26 );			set_register( pegasus, Gpio0, pegasus->features );			set_register( pegasus, Gpio0, DEFAULT_GPIO_SET );			break;		}	}	if ( i == REG_TIMEOUT )		return 1;	if ( usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS ||	     usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK1 ) {		__u16	auxmode;		read_mii_word( pegasus, 0, 0x1b, &auxmode );		write_mii_word( pegasus, 0, 0x1b, auxmode | 4 );	}	return	0;}static int enable_net_traffic( struct net_device *dev, struct usb_device *usb ){	__u16	linkpart, bmsr;	__u8	data[4];	pegasus_t *pegasus = dev->priv;	if ( read_mii_word(pegasus, pegasus->phy, MII_BMSR, &bmsr) ) 		return 1;	if ( !(bmsr & 0x20) && !loopback ) 		warn( "%s: link NOT established (0x%x) - check the cable.",			dev->name, bmsr );	if ( read_mii_word(pegasus, pegasus->phy, MII_ANLPA, &linkpart) )		return 2;	if ( !(linkpart & 1) )		warn( "link partner stat %x", linkpart );	data[0] = 0xc9;	data[1] = 0;	if ( linkpart & (ANLPA_100TX_FD | ANLPA_10T_FD) )		data[1] |= 0x20; /* set full duplex */	if ( linkpart & (ANLPA_100TX_FD | ANLPA_100TX_HD) )		data[1] |= 0x10; /* set 100 Mbps */	if ( mii_mode )		data[1] = 0;	data[2] = (loopback & 1) ? 0x09 : 0x01;	memcpy( pegasus->eth_regs, data, sizeof(data) );	set_registers( pegasus, EthCtrl0, 3, data );	return 0;}static void read_bulk_callback( struct urb *urb ){	pegasus_t *pegasus = urb->context;	struct net_device *net;	int count = urb->actual_length, res;	int rx_status;

⌨️ 快捷键说明

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