xref: /openbmc/linux/drivers/net/usb/pegasus.c (revision 5b2fc499917e5897a13add780e181b4cef197072)
1*5b2fc499SJeff Garzik /*
2*5b2fc499SJeff Garzik  *  Copyright (c) 1999-2005 Petko Manolov (petkan@users.sourceforge.net)
3*5b2fc499SJeff Garzik  *
4*5b2fc499SJeff Garzik  * This program is free software; you can redistribute it and/or modify
5*5b2fc499SJeff Garzik  * it under the terms of the GNU General Public License version 2 as
6*5b2fc499SJeff Garzik  * published by the Free Software Foundation.
7*5b2fc499SJeff Garzik  *
8*5b2fc499SJeff Garzik  *	ChangeLog:
9*5b2fc499SJeff Garzik  *		....	Most of the time spent on reading sources & docs.
10*5b2fc499SJeff Garzik  *		v0.2.x	First official release for the Linux kernel.
11*5b2fc499SJeff Garzik  *		v0.3.0	Beutified and structured, some bugs fixed.
12*5b2fc499SJeff Garzik  *		v0.3.x	URBifying bulk requests and bugfixing. First relatively
13*5b2fc499SJeff Garzik  *			stable release. Still can touch device's registers only
14*5b2fc499SJeff Garzik  *			from top-halves.
15*5b2fc499SJeff Garzik  *		v0.4.0	Control messages remained unurbified are now URBs.
16*5b2fc499SJeff Garzik  *			Now we can touch the HW at any time.
17*5b2fc499SJeff Garzik  *		v0.4.9	Control urbs again use process context to wait. Argh...
18*5b2fc499SJeff Garzik  *			Some long standing bugs (enable_net_traffic) fixed.
19*5b2fc499SJeff Garzik  *			Also nasty trick about resubmiting control urb from
20*5b2fc499SJeff Garzik  *			interrupt context used. Please let me know how it
21*5b2fc499SJeff Garzik  *			behaves. Pegasus II support added since this version.
22*5b2fc499SJeff Garzik  *			TODO: suppressing HCD warnings spewage on disconnect.
23*5b2fc499SJeff Garzik  *		v0.4.13	Ethernet address is now set at probe(), not at open()
24*5b2fc499SJeff Garzik  *			time as this seems to break dhcpd.
25*5b2fc499SJeff Garzik  *		v0.5.0	branch to 2.5.x kernels
26*5b2fc499SJeff Garzik  *		v0.5.1	ethtool support added
27*5b2fc499SJeff Garzik  *		v0.5.5	rx socket buffers are in a pool and the their allocation
28*5b2fc499SJeff Garzik  * 			is out of the interrupt routine.
29*5b2fc499SJeff Garzik  */
30*5b2fc499SJeff Garzik 
31*5b2fc499SJeff Garzik #include <linux/sched.h>
32*5b2fc499SJeff Garzik #include <linux/slab.h>
33*5b2fc499SJeff Garzik #include <linux/init.h>
34*5b2fc499SJeff Garzik #include <linux/delay.h>
35*5b2fc499SJeff Garzik #include <linux/netdevice.h>
36*5b2fc499SJeff Garzik #include <linux/etherdevice.h>
37*5b2fc499SJeff Garzik #include <linux/ethtool.h>
38*5b2fc499SJeff Garzik #include <linux/mii.h>
39*5b2fc499SJeff Garzik #include <linux/usb.h>
40*5b2fc499SJeff Garzik #include <linux/module.h>
41*5b2fc499SJeff Garzik #include <asm/byteorder.h>
42*5b2fc499SJeff Garzik #include <asm/uaccess.h>
43*5b2fc499SJeff Garzik #include "pegasus.h"
44*5b2fc499SJeff Garzik 
45*5b2fc499SJeff Garzik /*
46*5b2fc499SJeff Garzik  * Version Information
47*5b2fc499SJeff Garzik  */
48*5b2fc499SJeff Garzik #define DRIVER_VERSION "v0.6.14 (2006/09/27)"
49*5b2fc499SJeff Garzik #define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>"
50*5b2fc499SJeff Garzik #define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver"
51*5b2fc499SJeff Garzik 
52*5b2fc499SJeff Garzik static const char driver_name[] = "pegasus";
53*5b2fc499SJeff Garzik 
54*5b2fc499SJeff Garzik #undef	PEGASUS_WRITE_EEPROM
55*5b2fc499SJeff Garzik #define	BMSR_MEDIA	(BMSR_10HALF | BMSR_10FULL | BMSR_100HALF | \
56*5b2fc499SJeff Garzik 			BMSR_100FULL | BMSR_ANEGCAPABLE)
57*5b2fc499SJeff Garzik 
58*5b2fc499SJeff Garzik static int loopback = 0;
59*5b2fc499SJeff Garzik static int mii_mode = 0;
60*5b2fc499SJeff Garzik static char *devid=NULL;
61*5b2fc499SJeff Garzik 
62*5b2fc499SJeff Garzik static struct usb_eth_dev usb_dev_id[] = {
63*5b2fc499SJeff Garzik #define	PEGASUS_DEV(pn, vid, pid, flags)	\
64*5b2fc499SJeff Garzik 	{.name = pn, .vendor = vid, .device = pid, .private = flags},
65*5b2fc499SJeff Garzik #include "pegasus.h"
66*5b2fc499SJeff Garzik #undef	PEGASUS_DEV
67*5b2fc499SJeff Garzik 	{NULL, 0, 0, 0},
68*5b2fc499SJeff Garzik 	{NULL, 0, 0, 0}
69*5b2fc499SJeff Garzik };
70*5b2fc499SJeff Garzik 
71*5b2fc499SJeff Garzik static struct usb_device_id pegasus_ids[] = {
72*5b2fc499SJeff Garzik #define	PEGASUS_DEV(pn, vid, pid, flags) \
73*5b2fc499SJeff Garzik 	{.match_flags = USB_DEVICE_ID_MATCH_DEVICE, .idVendor = vid, .idProduct = pid},
74*5b2fc499SJeff Garzik #include "pegasus.h"
75*5b2fc499SJeff Garzik #undef	PEGASUS_DEV
76*5b2fc499SJeff Garzik 	{},
77*5b2fc499SJeff Garzik 	{}
78*5b2fc499SJeff Garzik };
79*5b2fc499SJeff Garzik 
80*5b2fc499SJeff Garzik MODULE_AUTHOR(DRIVER_AUTHOR);
81*5b2fc499SJeff Garzik MODULE_DESCRIPTION(DRIVER_DESC);
82*5b2fc499SJeff Garzik MODULE_LICENSE("GPL");
83*5b2fc499SJeff Garzik module_param(loopback, bool, 0);
84*5b2fc499SJeff Garzik module_param(mii_mode, bool, 0);
85*5b2fc499SJeff Garzik module_param(devid, charp, 0);
86*5b2fc499SJeff Garzik MODULE_PARM_DESC(loopback, "Enable MAC loopback mode (bit 0)");
87*5b2fc499SJeff Garzik MODULE_PARM_DESC(mii_mode, "Enable HomePNA mode (bit 0),default=MII mode = 0");
88*5b2fc499SJeff Garzik MODULE_PARM_DESC(devid, "The format is: 'DEV_name:VendorID:DeviceID:Flags'");
89*5b2fc499SJeff Garzik 
90*5b2fc499SJeff Garzik /* use ethtool to change the level for any given device */
91*5b2fc499SJeff Garzik static int msg_level = -1;
92*5b2fc499SJeff Garzik module_param (msg_level, int, 0);
93*5b2fc499SJeff Garzik MODULE_PARM_DESC (msg_level, "Override default message level");
94*5b2fc499SJeff Garzik 
95*5b2fc499SJeff Garzik MODULE_DEVICE_TABLE(usb, pegasus_ids);
96*5b2fc499SJeff Garzik 
97*5b2fc499SJeff Garzik static int update_eth_regs_async(pegasus_t *);
98*5b2fc499SJeff Garzik /* Aargh!!! I _really_ hate such tweaks */
99*5b2fc499SJeff Garzik static void ctrl_callback(struct urb *urb)
100*5b2fc499SJeff Garzik {
101*5b2fc499SJeff Garzik 	pegasus_t *pegasus = urb->context;
102*5b2fc499SJeff Garzik 
103*5b2fc499SJeff Garzik 	if (!pegasus)
104*5b2fc499SJeff Garzik 		return;
105*5b2fc499SJeff Garzik 
106*5b2fc499SJeff Garzik 	switch (urb->status) {
107*5b2fc499SJeff Garzik 	case 0:
108*5b2fc499SJeff Garzik 		if (pegasus->flags & ETH_REGS_CHANGE) {
109*5b2fc499SJeff Garzik 			pegasus->flags &= ~ETH_REGS_CHANGE;
110*5b2fc499SJeff Garzik 			pegasus->flags |= ETH_REGS_CHANGED;
111*5b2fc499SJeff Garzik 			update_eth_regs_async(pegasus);
112*5b2fc499SJeff Garzik 			return;
113*5b2fc499SJeff Garzik 		}
114*5b2fc499SJeff Garzik 		break;
115*5b2fc499SJeff Garzik 	case -EINPROGRESS:
116*5b2fc499SJeff Garzik 		return;
117*5b2fc499SJeff Garzik 	case -ENOENT:
118*5b2fc499SJeff Garzik 		break;
119*5b2fc499SJeff Garzik 	default:
120*5b2fc499SJeff Garzik 		if (netif_msg_drv(pegasus))
121*5b2fc499SJeff Garzik 			dev_dbg(&pegasus->intf->dev, "%s, status %d\n",
122*5b2fc499SJeff Garzik 				__FUNCTION__, urb->status);
123*5b2fc499SJeff Garzik 	}
124*5b2fc499SJeff Garzik 	pegasus->flags &= ~ETH_REGS_CHANGED;
125*5b2fc499SJeff Garzik 	wake_up(&pegasus->ctrl_wait);
126*5b2fc499SJeff Garzik }
127*5b2fc499SJeff Garzik 
128*5b2fc499SJeff Garzik static int get_registers(pegasus_t * pegasus, __u16 indx, __u16 size,
129*5b2fc499SJeff Garzik 			 void *data)
130*5b2fc499SJeff Garzik {
131*5b2fc499SJeff Garzik 	int ret;
132*5b2fc499SJeff Garzik 	char *buffer;
133*5b2fc499SJeff Garzik 	DECLARE_WAITQUEUE(wait, current);
134*5b2fc499SJeff Garzik 
135*5b2fc499SJeff Garzik 	buffer = kmalloc(size, GFP_KERNEL);
136*5b2fc499SJeff Garzik 	if (!buffer) {
137*5b2fc499SJeff Garzik 		if (netif_msg_drv(pegasus))
138*5b2fc499SJeff Garzik 			dev_warn(&pegasus->intf->dev, "out of memory in %s\n",
139*5b2fc499SJeff Garzik 					__FUNCTION__);
140*5b2fc499SJeff Garzik 		return -ENOMEM;
141*5b2fc499SJeff Garzik 	}
142*5b2fc499SJeff Garzik 	add_wait_queue(&pegasus->ctrl_wait, &wait);
143*5b2fc499SJeff Garzik 	set_current_state(TASK_UNINTERRUPTIBLE);
144*5b2fc499SJeff Garzik 	while (pegasus->flags & ETH_REGS_CHANGED)
145*5b2fc499SJeff Garzik 		schedule();
146*5b2fc499SJeff Garzik 	remove_wait_queue(&pegasus->ctrl_wait, &wait);
147*5b2fc499SJeff Garzik 	set_current_state(TASK_RUNNING);
148*5b2fc499SJeff Garzik 
149*5b2fc499SJeff Garzik 	pegasus->dr.bRequestType = PEGASUS_REQT_READ;
150*5b2fc499SJeff Garzik 	pegasus->dr.bRequest = PEGASUS_REQ_GET_REGS;
151*5b2fc499SJeff Garzik 	pegasus->dr.wValue = cpu_to_le16(0);
152*5b2fc499SJeff Garzik 	pegasus->dr.wIndex = cpu_to_le16p(&indx);
153*5b2fc499SJeff Garzik 	pegasus->dr.wLength = cpu_to_le16p(&size);
154*5b2fc499SJeff Garzik 	pegasus->ctrl_urb->transfer_buffer_length = size;
155*5b2fc499SJeff Garzik 
156*5b2fc499SJeff Garzik 	usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb,
157*5b2fc499SJeff Garzik 			     usb_rcvctrlpipe(pegasus->usb, 0),
158*5b2fc499SJeff Garzik 			     (char *) &pegasus->dr,
159*5b2fc499SJeff Garzik 			     buffer, size, ctrl_callback, pegasus);
160*5b2fc499SJeff Garzik 
161*5b2fc499SJeff Garzik 	add_wait_queue(&pegasus->ctrl_wait, &wait);
162*5b2fc499SJeff Garzik 	set_current_state(TASK_UNINTERRUPTIBLE);
163*5b2fc499SJeff Garzik 
164*5b2fc499SJeff Garzik 	/* using ATOMIC, we'd never wake up if we slept */
165*5b2fc499SJeff Garzik 	if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
166*5b2fc499SJeff Garzik 		set_current_state(TASK_RUNNING);
167*5b2fc499SJeff Garzik 		if (ret == -ENODEV)
168*5b2fc499SJeff Garzik 			netif_device_detach(pegasus->net);
169*5b2fc499SJeff Garzik 		if (netif_msg_drv(pegasus))
170*5b2fc499SJeff Garzik 			dev_err(&pegasus->intf->dev, "%s, status %d\n",
171*5b2fc499SJeff Garzik 					__FUNCTION__, ret);
172*5b2fc499SJeff Garzik 		goto out;
173*5b2fc499SJeff Garzik 	}
174*5b2fc499SJeff Garzik 
175*5b2fc499SJeff Garzik 	schedule();
176*5b2fc499SJeff Garzik out:
177*5b2fc499SJeff Garzik 	remove_wait_queue(&pegasus->ctrl_wait, &wait);
178*5b2fc499SJeff Garzik 	memcpy(data, buffer, size);
179*5b2fc499SJeff Garzik 	kfree(buffer);
180*5b2fc499SJeff Garzik 
181*5b2fc499SJeff Garzik 	return ret;
182*5b2fc499SJeff Garzik }
183*5b2fc499SJeff Garzik 
184*5b2fc499SJeff Garzik static int set_registers(pegasus_t * pegasus, __u16 indx, __u16 size,
185*5b2fc499SJeff Garzik 			 void *data)
186*5b2fc499SJeff Garzik {
187*5b2fc499SJeff Garzik 	int ret;
188*5b2fc499SJeff Garzik 	char *buffer;
189*5b2fc499SJeff Garzik 	DECLARE_WAITQUEUE(wait, current);
190*5b2fc499SJeff Garzik 
191*5b2fc499SJeff Garzik 	buffer = kmalloc(size, GFP_KERNEL);
192*5b2fc499SJeff Garzik 	if (!buffer) {
193*5b2fc499SJeff Garzik 		if (netif_msg_drv(pegasus))
194*5b2fc499SJeff Garzik 			dev_warn(&pegasus->intf->dev, "out of memory in %s\n",
195*5b2fc499SJeff Garzik 					__FUNCTION__);
196*5b2fc499SJeff Garzik 		return -ENOMEM;
197*5b2fc499SJeff Garzik 	}
198*5b2fc499SJeff Garzik 	memcpy(buffer, data, size);
199*5b2fc499SJeff Garzik 
200*5b2fc499SJeff Garzik 	add_wait_queue(&pegasus->ctrl_wait, &wait);
201*5b2fc499SJeff Garzik 	set_current_state(TASK_UNINTERRUPTIBLE);
202*5b2fc499SJeff Garzik 	while (pegasus->flags & ETH_REGS_CHANGED)
203*5b2fc499SJeff Garzik 		schedule();
204*5b2fc499SJeff Garzik 	remove_wait_queue(&pegasus->ctrl_wait, &wait);
205*5b2fc499SJeff Garzik 	set_current_state(TASK_RUNNING);
206*5b2fc499SJeff Garzik 
207*5b2fc499SJeff Garzik 	pegasus->dr.bRequestType = PEGASUS_REQT_WRITE;
208*5b2fc499SJeff Garzik 	pegasus->dr.bRequest = PEGASUS_REQ_SET_REGS;
209*5b2fc499SJeff Garzik 	pegasus->dr.wValue = cpu_to_le16(0);
210*5b2fc499SJeff Garzik 	pegasus->dr.wIndex = cpu_to_le16p(&indx);
211*5b2fc499SJeff Garzik 	pegasus->dr.wLength = cpu_to_le16p(&size);
212*5b2fc499SJeff Garzik 	pegasus->ctrl_urb->transfer_buffer_length = size;
213*5b2fc499SJeff Garzik 
214*5b2fc499SJeff Garzik 	usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb,
215*5b2fc499SJeff Garzik 			     usb_sndctrlpipe(pegasus->usb, 0),
216*5b2fc499SJeff Garzik 			     (char *) &pegasus->dr,
217*5b2fc499SJeff Garzik 			     buffer, size, ctrl_callback, pegasus);
218*5b2fc499SJeff Garzik 
219*5b2fc499SJeff Garzik 	add_wait_queue(&pegasus->ctrl_wait, &wait);
220*5b2fc499SJeff Garzik 	set_current_state(TASK_UNINTERRUPTIBLE);
221*5b2fc499SJeff Garzik 
222*5b2fc499SJeff Garzik 	if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
223*5b2fc499SJeff Garzik 		if (ret == -ENODEV)
224*5b2fc499SJeff Garzik 			netif_device_detach(pegasus->net);
225*5b2fc499SJeff Garzik 		if (netif_msg_drv(pegasus))
226*5b2fc499SJeff Garzik 			dev_err(&pegasus->intf->dev, "%s, status %d\n",
227*5b2fc499SJeff Garzik 					__FUNCTION__, ret);
228*5b2fc499SJeff Garzik 		goto out;
229*5b2fc499SJeff Garzik 	}
230*5b2fc499SJeff Garzik 
231*5b2fc499SJeff Garzik 	schedule();
232*5b2fc499SJeff Garzik out:
233*5b2fc499SJeff Garzik 	remove_wait_queue(&pegasus->ctrl_wait, &wait);
234*5b2fc499SJeff Garzik 	kfree(buffer);
235*5b2fc499SJeff Garzik 
236*5b2fc499SJeff Garzik 	return ret;
237*5b2fc499SJeff Garzik }
238*5b2fc499SJeff Garzik 
239*5b2fc499SJeff Garzik static int set_register(pegasus_t * pegasus, __u16 indx, __u8 data)
240*5b2fc499SJeff Garzik {
241*5b2fc499SJeff Garzik 	int ret;
242*5b2fc499SJeff Garzik 	char *tmp;
243*5b2fc499SJeff Garzik 	DECLARE_WAITQUEUE(wait, current);
244*5b2fc499SJeff Garzik 
245*5b2fc499SJeff Garzik 	tmp = kmalloc(1, GFP_KERNEL);
246*5b2fc499SJeff Garzik 	if (!tmp) {
247*5b2fc499SJeff Garzik 		if (netif_msg_drv(pegasus))
248*5b2fc499SJeff Garzik 			dev_warn(&pegasus->intf->dev, "out of memory in %s\n",
249*5b2fc499SJeff Garzik 					__FUNCTION__);
250*5b2fc499SJeff Garzik 		return -ENOMEM;
251*5b2fc499SJeff Garzik 	}
252*5b2fc499SJeff Garzik 	memcpy(tmp, &data, 1);
253*5b2fc499SJeff Garzik 	add_wait_queue(&pegasus->ctrl_wait, &wait);
254*5b2fc499SJeff Garzik 	set_current_state(TASK_UNINTERRUPTIBLE);
255*5b2fc499SJeff Garzik 	while (pegasus->flags & ETH_REGS_CHANGED)
256*5b2fc499SJeff Garzik 		schedule();
257*5b2fc499SJeff Garzik 	remove_wait_queue(&pegasus->ctrl_wait, &wait);
258*5b2fc499SJeff Garzik 	set_current_state(TASK_RUNNING);
259*5b2fc499SJeff Garzik 
260*5b2fc499SJeff Garzik 	pegasus->dr.bRequestType = PEGASUS_REQT_WRITE;
261*5b2fc499SJeff Garzik 	pegasus->dr.bRequest = PEGASUS_REQ_SET_REG;
262*5b2fc499SJeff Garzik 	pegasus->dr.wValue = cpu_to_le16(data);
263*5b2fc499SJeff Garzik 	pegasus->dr.wIndex = cpu_to_le16p(&indx);
264*5b2fc499SJeff Garzik 	pegasus->dr.wLength = cpu_to_le16(1);
265*5b2fc499SJeff Garzik 	pegasus->ctrl_urb->transfer_buffer_length = 1;
266*5b2fc499SJeff Garzik 
267*5b2fc499SJeff Garzik 	usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb,
268*5b2fc499SJeff Garzik 			     usb_sndctrlpipe(pegasus->usb, 0),
269*5b2fc499SJeff Garzik 			     (char *) &pegasus->dr,
270*5b2fc499SJeff Garzik 			     tmp, 1, ctrl_callback, pegasus);
271*5b2fc499SJeff Garzik 
272*5b2fc499SJeff Garzik 	add_wait_queue(&pegasus->ctrl_wait, &wait);
273*5b2fc499SJeff Garzik 	set_current_state(TASK_UNINTERRUPTIBLE);
274*5b2fc499SJeff Garzik 
275*5b2fc499SJeff Garzik 	if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
276*5b2fc499SJeff Garzik 		if (ret == -ENODEV)
277*5b2fc499SJeff Garzik 			netif_device_detach(pegasus->net);
278*5b2fc499SJeff Garzik 		if (netif_msg_drv(pegasus))
279*5b2fc499SJeff Garzik 			dev_err(&pegasus->intf->dev, "%s, status %d\n",
280*5b2fc499SJeff Garzik 					__FUNCTION__, ret);
281*5b2fc499SJeff Garzik 		goto out;
282*5b2fc499SJeff Garzik 	}
283*5b2fc499SJeff Garzik 
284*5b2fc499SJeff Garzik 	schedule();
285*5b2fc499SJeff Garzik out:
286*5b2fc499SJeff Garzik 	remove_wait_queue(&pegasus->ctrl_wait, &wait);
287*5b2fc499SJeff Garzik 	kfree(tmp);
288*5b2fc499SJeff Garzik 
289*5b2fc499SJeff Garzik 	return ret;
290*5b2fc499SJeff Garzik }
291*5b2fc499SJeff Garzik 
292*5b2fc499SJeff Garzik static int update_eth_regs_async(pegasus_t * pegasus)
293*5b2fc499SJeff Garzik {
294*5b2fc499SJeff Garzik 	int ret;
295*5b2fc499SJeff Garzik 
296*5b2fc499SJeff Garzik 	pegasus->dr.bRequestType = PEGASUS_REQT_WRITE;
297*5b2fc499SJeff Garzik 	pegasus->dr.bRequest = PEGASUS_REQ_SET_REGS;
298*5b2fc499SJeff Garzik 	pegasus->dr.wValue = 0;
299*5b2fc499SJeff Garzik 	pegasus->dr.wIndex = cpu_to_le16(EthCtrl0);
300*5b2fc499SJeff Garzik 	pegasus->dr.wLength = cpu_to_le16(3);
301*5b2fc499SJeff Garzik 	pegasus->ctrl_urb->transfer_buffer_length = 3;
302*5b2fc499SJeff Garzik 
303*5b2fc499SJeff Garzik 	usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb,
304*5b2fc499SJeff Garzik 			     usb_sndctrlpipe(pegasus->usb, 0),
305*5b2fc499SJeff Garzik 			     (char *) &pegasus->dr,
306*5b2fc499SJeff Garzik 			     pegasus->eth_regs, 3, ctrl_callback, pegasus);
307*5b2fc499SJeff Garzik 
308*5b2fc499SJeff Garzik 	if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
309*5b2fc499SJeff Garzik 		if (ret == -ENODEV)
310*5b2fc499SJeff Garzik 			netif_device_detach(pegasus->net);
311*5b2fc499SJeff Garzik 		if (netif_msg_drv(pegasus))
312*5b2fc499SJeff Garzik 			dev_err(&pegasus->intf->dev, "%s, status %d\n",
313*5b2fc499SJeff Garzik 					__FUNCTION__, ret);
314*5b2fc499SJeff Garzik 	}
315*5b2fc499SJeff Garzik 
316*5b2fc499SJeff Garzik 	return ret;
317*5b2fc499SJeff Garzik }
318*5b2fc499SJeff Garzik 
319*5b2fc499SJeff Garzik /* Returns 0 on success, error on failure */
320*5b2fc499SJeff Garzik static int read_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 * regd)
321*5b2fc499SJeff Garzik {
322*5b2fc499SJeff Garzik 	int i;
323*5b2fc499SJeff Garzik 	__u8 data[4] = { phy, 0, 0, indx };
324*5b2fc499SJeff Garzik 	__le16 regdi;
325*5b2fc499SJeff Garzik 	int ret;
326*5b2fc499SJeff Garzik 
327*5b2fc499SJeff Garzik 	set_register(pegasus, PhyCtrl, 0);
328*5b2fc499SJeff Garzik 	set_registers(pegasus, PhyAddr, sizeof (data), data);
329*5b2fc499SJeff Garzik 	set_register(pegasus, PhyCtrl, (indx | PHY_READ));
330*5b2fc499SJeff Garzik 	for (i = 0; i < REG_TIMEOUT; i++) {
331*5b2fc499SJeff Garzik 		ret = get_registers(pegasus, PhyCtrl, 1, data);
332*5b2fc499SJeff Garzik 		if (ret == -ESHUTDOWN)
333*5b2fc499SJeff Garzik 			goto fail;
334*5b2fc499SJeff Garzik 		if (data[0] & PHY_DONE)
335*5b2fc499SJeff Garzik 			break;
336*5b2fc499SJeff Garzik 	}
337*5b2fc499SJeff Garzik 	if (i < REG_TIMEOUT) {
338*5b2fc499SJeff Garzik 		ret = get_registers(pegasus, PhyData, 2, &regdi);
339*5b2fc499SJeff Garzik 		*regd = le16_to_cpu(regdi);
340*5b2fc499SJeff Garzik 		return ret;
341*5b2fc499SJeff Garzik 	}
342*5b2fc499SJeff Garzik fail:
343*5b2fc499SJeff Garzik 	if (netif_msg_drv(pegasus))
344*5b2fc499SJeff Garzik 		dev_warn(&pegasus->intf->dev, "%s failed\n", __FUNCTION__);
345*5b2fc499SJeff Garzik 
346*5b2fc499SJeff Garzik 	return ret;
347*5b2fc499SJeff Garzik }
348*5b2fc499SJeff Garzik 
349*5b2fc499SJeff Garzik static int mdio_read(struct net_device *dev, int phy_id, int loc)
350*5b2fc499SJeff Garzik {
351*5b2fc499SJeff Garzik 	pegasus_t *pegasus = (pegasus_t *) netdev_priv(dev);
352*5b2fc499SJeff Garzik 	u16 res;
353*5b2fc499SJeff Garzik 
354*5b2fc499SJeff Garzik 	read_mii_word(pegasus, phy_id, loc, &res);
355*5b2fc499SJeff Garzik 	return (int)res;
356*5b2fc499SJeff Garzik }
357*5b2fc499SJeff Garzik 
358*5b2fc499SJeff Garzik static int write_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 regd)
359*5b2fc499SJeff Garzik {
360*5b2fc499SJeff Garzik 	int i;
361*5b2fc499SJeff Garzik 	__u8 data[4] = { phy, 0, 0, indx };
362*5b2fc499SJeff Garzik 	int ret;
363*5b2fc499SJeff Garzik 
364*5b2fc499SJeff Garzik 	data[1] = (u8) regd;
365*5b2fc499SJeff Garzik 	data[2] = (u8) (regd >> 8);
366*5b2fc499SJeff Garzik 	set_register(pegasus, PhyCtrl, 0);
367*5b2fc499SJeff Garzik 	set_registers(pegasus, PhyAddr, sizeof(data), data);
368*5b2fc499SJeff Garzik 	set_register(pegasus, PhyCtrl, (indx | PHY_WRITE));
369*5b2fc499SJeff Garzik 	for (i = 0; i < REG_TIMEOUT; i++) {
370*5b2fc499SJeff Garzik 		ret = get_registers(pegasus, PhyCtrl, 1, data);
371*5b2fc499SJeff Garzik 		if (ret == -ESHUTDOWN)
372*5b2fc499SJeff Garzik 			goto fail;
373*5b2fc499SJeff Garzik 		if (data[0] & PHY_DONE)
374*5b2fc499SJeff Garzik 			break;
375*5b2fc499SJeff Garzik 	}
376*5b2fc499SJeff Garzik 	if (i < REG_TIMEOUT)
377*5b2fc499SJeff Garzik 		return ret;
378*5b2fc499SJeff Garzik 
379*5b2fc499SJeff Garzik fail:
380*5b2fc499SJeff Garzik 	if (netif_msg_drv(pegasus))
381*5b2fc499SJeff Garzik 		dev_warn(&pegasus->intf->dev, "%s failed\n", __FUNCTION__);
382*5b2fc499SJeff Garzik 	return -ETIMEDOUT;
383*5b2fc499SJeff Garzik }
384*5b2fc499SJeff Garzik 
385*5b2fc499SJeff Garzik static void mdio_write(struct net_device *dev, int phy_id, int loc, int val)
386*5b2fc499SJeff Garzik {
387*5b2fc499SJeff Garzik 	pegasus_t *pegasus = (pegasus_t *) netdev_priv(dev);
388*5b2fc499SJeff Garzik 
389*5b2fc499SJeff Garzik 	write_mii_word(pegasus, phy_id, loc, val);
390*5b2fc499SJeff Garzik }
391*5b2fc499SJeff Garzik 
392*5b2fc499SJeff Garzik static int read_eprom_word(pegasus_t * pegasus, __u8 index, __u16 * retdata)
393*5b2fc499SJeff Garzik {
394*5b2fc499SJeff Garzik 	int i;
395*5b2fc499SJeff Garzik 	__u8 tmp;
396*5b2fc499SJeff Garzik 	__le16 retdatai;
397*5b2fc499SJeff Garzik 	int ret;
398*5b2fc499SJeff Garzik 
399*5b2fc499SJeff Garzik 	set_register(pegasus, EpromCtrl, 0);
400*5b2fc499SJeff Garzik 	set_register(pegasus, EpromOffset, index);
401*5b2fc499SJeff Garzik 	set_register(pegasus, EpromCtrl, EPROM_READ);
402*5b2fc499SJeff Garzik 
403*5b2fc499SJeff Garzik 	for (i = 0; i < REG_TIMEOUT; i++) {
404*5b2fc499SJeff Garzik 		ret = get_registers(pegasus, EpromCtrl, 1, &tmp);
405*5b2fc499SJeff Garzik 		if (tmp & EPROM_DONE)
406*5b2fc499SJeff Garzik 			break;
407*5b2fc499SJeff Garzik 		if (ret == -ESHUTDOWN)
408*5b2fc499SJeff Garzik 			goto fail;
409*5b2fc499SJeff Garzik 	}
410*5b2fc499SJeff Garzik 	if (i < REG_TIMEOUT) {
411*5b2fc499SJeff Garzik 		ret = get_registers(pegasus, EpromData, 2, &retdatai);
412*5b2fc499SJeff Garzik 		*retdata = le16_to_cpu(retdatai);
413*5b2fc499SJeff Garzik 		return ret;
414*5b2fc499SJeff Garzik 	}
415*5b2fc499SJeff Garzik 
416*5b2fc499SJeff Garzik fail:
417*5b2fc499SJeff Garzik 	if (netif_msg_drv(pegasus))
418*5b2fc499SJeff Garzik 		dev_warn(&pegasus->intf->dev, "%s failed\n", __FUNCTION__);
419*5b2fc499SJeff Garzik 	return -ETIMEDOUT;
420*5b2fc499SJeff Garzik }
421*5b2fc499SJeff Garzik 
422*5b2fc499SJeff Garzik #ifdef	PEGASUS_WRITE_EEPROM
423*5b2fc499SJeff Garzik static inline void enable_eprom_write(pegasus_t * pegasus)
424*5b2fc499SJeff Garzik {
425*5b2fc499SJeff Garzik 	__u8 tmp;
426*5b2fc499SJeff Garzik 	int ret;
427*5b2fc499SJeff Garzik 
428*5b2fc499SJeff Garzik 	get_registers(pegasus, EthCtrl2, 1, &tmp);
429*5b2fc499SJeff Garzik 	set_register(pegasus, EthCtrl2, tmp | EPROM_WR_ENABLE);
430*5b2fc499SJeff Garzik }
431*5b2fc499SJeff Garzik 
432*5b2fc499SJeff Garzik static inline void disable_eprom_write(pegasus_t * pegasus)
433*5b2fc499SJeff Garzik {
434*5b2fc499SJeff Garzik 	__u8 tmp;
435*5b2fc499SJeff Garzik 	int ret;
436*5b2fc499SJeff Garzik 
437*5b2fc499SJeff Garzik 	get_registers(pegasus, EthCtrl2, 1, &tmp);
438*5b2fc499SJeff Garzik 	set_register(pegasus, EpromCtrl, 0);
439*5b2fc499SJeff Garzik 	set_register(pegasus, EthCtrl2, tmp & ~EPROM_WR_ENABLE);
440*5b2fc499SJeff Garzik }
441*5b2fc499SJeff Garzik 
442*5b2fc499SJeff Garzik static int write_eprom_word(pegasus_t * pegasus, __u8 index, __u16 data)
443*5b2fc499SJeff Garzik {
444*5b2fc499SJeff Garzik 	int i;
445*5b2fc499SJeff Garzik 	__u8 tmp, d[4] = { 0x3f, 0, 0, EPROM_WRITE };
446*5b2fc499SJeff Garzik 	int ret;
447*5b2fc499SJeff Garzik 
448*5b2fc499SJeff Garzik 	set_registers(pegasus, EpromOffset, 4, d);
449*5b2fc499SJeff Garzik 	enable_eprom_write(pegasus);
450*5b2fc499SJeff Garzik 	set_register(pegasus, EpromOffset, index);
451*5b2fc499SJeff Garzik 	set_registers(pegasus, EpromData, 2, &data);
452*5b2fc499SJeff Garzik 	set_register(pegasus, EpromCtrl, EPROM_WRITE);
453*5b2fc499SJeff Garzik 
454*5b2fc499SJeff Garzik 	for (i = 0; i < REG_TIMEOUT; i++) {
455*5b2fc499SJeff Garzik 		ret = get_registers(pegasus, EpromCtrl, 1, &tmp);
456*5b2fc499SJeff Garzik 		if (ret == -ESHUTDOWN)
457*5b2fc499SJeff Garzik 			goto fail;
458*5b2fc499SJeff Garzik 		if (tmp & EPROM_DONE)
459*5b2fc499SJeff Garzik 			break;
460*5b2fc499SJeff Garzik 	}
461*5b2fc499SJeff Garzik 	disable_eprom_write(pegasus);
462*5b2fc499SJeff Garzik 	if (i < REG_TIMEOUT)
463*5b2fc499SJeff Garzik 		return ret;
464*5b2fc499SJeff Garzik fail:
465*5b2fc499SJeff Garzik 	if (netif_msg_drv(pegasus))
466*5b2fc499SJeff Garzik 		dev_warn(&pegasus->intf->dev, "%s failed\n", __FUNCTION__);
467*5b2fc499SJeff Garzik 	return -ETIMEDOUT;
468*5b2fc499SJeff Garzik }
469*5b2fc499SJeff Garzik #endif				/* PEGASUS_WRITE_EEPROM */
470*5b2fc499SJeff Garzik 
471*5b2fc499SJeff Garzik static inline void get_node_id(pegasus_t * pegasus, __u8 * id)
472*5b2fc499SJeff Garzik {
473*5b2fc499SJeff Garzik 	int i;
474*5b2fc499SJeff Garzik 	__u16 w16;
475*5b2fc499SJeff Garzik 
476*5b2fc499SJeff Garzik 	for (i = 0; i < 3; i++) {
477*5b2fc499SJeff Garzik 		read_eprom_word(pegasus, i, &w16);
478*5b2fc499SJeff Garzik 		((__le16 *) id)[i] = cpu_to_le16p(&w16);
479*5b2fc499SJeff Garzik 	}
480*5b2fc499SJeff Garzik }
481*5b2fc499SJeff Garzik 
482*5b2fc499SJeff Garzik static void set_ethernet_addr(pegasus_t * pegasus)
483*5b2fc499SJeff Garzik {
484*5b2fc499SJeff Garzik 	__u8 node_id[6];
485*5b2fc499SJeff Garzik 
486*5b2fc499SJeff Garzik 	if (pegasus->features & PEGASUS_II) {
487*5b2fc499SJeff Garzik 		get_registers(pegasus, 0x10, sizeof(node_id), node_id);
488*5b2fc499SJeff Garzik 	} else {
489*5b2fc499SJeff Garzik 		get_node_id(pegasus, node_id);
490*5b2fc499SJeff Garzik 		set_registers(pegasus, EthID, sizeof (node_id), node_id);
491*5b2fc499SJeff Garzik 	}
492*5b2fc499SJeff Garzik 	memcpy(pegasus->net->dev_addr, node_id, sizeof (node_id));
493*5b2fc499SJeff Garzik }
494*5b2fc499SJeff Garzik 
495*5b2fc499SJeff Garzik static inline int reset_mac(pegasus_t * pegasus)
496*5b2fc499SJeff Garzik {
497*5b2fc499SJeff Garzik 	__u8 data = 0x8;
498*5b2fc499SJeff Garzik 	int i;
499*5b2fc499SJeff Garzik 
500*5b2fc499SJeff Garzik 	set_register(pegasus, EthCtrl1, data);
501*5b2fc499SJeff Garzik 	for (i = 0; i < REG_TIMEOUT; i++) {
502*5b2fc499SJeff Garzik 		get_registers(pegasus, EthCtrl1, 1, &data);
503*5b2fc499SJeff Garzik 		if (~data & 0x08) {
504*5b2fc499SJeff Garzik 			if (loopback & 1)
505*5b2fc499SJeff Garzik 				break;
506*5b2fc499SJeff Garzik 			if (mii_mode && (pegasus->features & HAS_HOME_PNA))
507*5b2fc499SJeff Garzik 				set_register(pegasus, Gpio1, 0x34);
508*5b2fc499SJeff Garzik 			else
509*5b2fc499SJeff Garzik 				set_register(pegasus, Gpio1, 0x26);
510*5b2fc499SJeff Garzik 			set_register(pegasus, Gpio0, pegasus->features);
511*5b2fc499SJeff Garzik 			set_register(pegasus, Gpio0, DEFAULT_GPIO_SET);
512*5b2fc499SJeff Garzik 			break;
513*5b2fc499SJeff Garzik 		}
514*5b2fc499SJeff Garzik 	}
515*5b2fc499SJeff Garzik 	if (i == REG_TIMEOUT)
516*5b2fc499SJeff Garzik 		return -ETIMEDOUT;
517*5b2fc499SJeff Garzik 
518*5b2fc499SJeff Garzik 	if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS ||
519*5b2fc499SJeff Garzik 	    usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) {
520*5b2fc499SJeff Garzik 		set_register(pegasus, Gpio0, 0x24);
521*5b2fc499SJeff Garzik 		set_register(pegasus, Gpio0, 0x26);
522*5b2fc499SJeff Garzik 	}
523*5b2fc499SJeff Garzik 	if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_ELCON) {
524*5b2fc499SJeff Garzik 		__u16 auxmode;
525*5b2fc499SJeff Garzik 		read_mii_word(pegasus, 3, 0x1b, &auxmode);
526*5b2fc499SJeff Garzik 		write_mii_word(pegasus, 3, 0x1b, auxmode | 4);
527*5b2fc499SJeff Garzik 	}
528*5b2fc499SJeff Garzik 
529*5b2fc499SJeff Garzik 	return 0;
530*5b2fc499SJeff Garzik }
531*5b2fc499SJeff Garzik 
532*5b2fc499SJeff Garzik static int enable_net_traffic(struct net_device *dev, struct usb_device *usb)
533*5b2fc499SJeff Garzik {
534*5b2fc499SJeff Garzik 	__u16 linkpart;
535*5b2fc499SJeff Garzik 	__u8 data[4];
536*5b2fc499SJeff Garzik 	pegasus_t *pegasus = netdev_priv(dev);
537*5b2fc499SJeff Garzik 	int ret;
538*5b2fc499SJeff Garzik 
539*5b2fc499SJeff Garzik 	read_mii_word(pegasus, pegasus->phy, MII_LPA, &linkpart);
540*5b2fc499SJeff Garzik 	data[0] = 0xc9;
541*5b2fc499SJeff Garzik 	data[1] = 0;
542*5b2fc499SJeff Garzik 	if (linkpart & (ADVERTISE_100FULL | ADVERTISE_10FULL))
543*5b2fc499SJeff Garzik 		data[1] |= 0x20;	/* set full duplex */
544*5b2fc499SJeff Garzik 	if (linkpart & (ADVERTISE_100FULL | ADVERTISE_100HALF))
545*5b2fc499SJeff Garzik 		data[1] |= 0x10;	/* set 100 Mbps */
546*5b2fc499SJeff Garzik 	if (mii_mode)
547*5b2fc499SJeff Garzik 		data[1] = 0;
548*5b2fc499SJeff Garzik 	data[2] = (loopback & 1) ? 0x09 : 0x01;
549*5b2fc499SJeff Garzik 
550*5b2fc499SJeff Garzik 	memcpy(pegasus->eth_regs, data, sizeof (data));
551*5b2fc499SJeff Garzik 	ret = set_registers(pegasus, EthCtrl0, 3, data);
552*5b2fc499SJeff Garzik 
553*5b2fc499SJeff Garzik 	if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS ||
554*5b2fc499SJeff Garzik 	    usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS2 ||
555*5b2fc499SJeff Garzik 	    usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) {
556*5b2fc499SJeff Garzik 		u16 auxmode;
557*5b2fc499SJeff Garzik 		read_mii_word(pegasus, 0, 0x1b, &auxmode);
558*5b2fc499SJeff Garzik 		write_mii_word(pegasus, 0, 0x1b, auxmode | 4);
559*5b2fc499SJeff Garzik 	}
560*5b2fc499SJeff Garzik 
561*5b2fc499SJeff Garzik 	return ret;
562*5b2fc499SJeff Garzik }
563*5b2fc499SJeff Garzik 
564*5b2fc499SJeff Garzik static void fill_skb_pool(pegasus_t * pegasus)
565*5b2fc499SJeff Garzik {
566*5b2fc499SJeff Garzik 	int i;
567*5b2fc499SJeff Garzik 
568*5b2fc499SJeff Garzik 	for (i = 0; i < RX_SKBS; i++) {
569*5b2fc499SJeff Garzik 		if (pegasus->rx_pool[i])
570*5b2fc499SJeff Garzik 			continue;
571*5b2fc499SJeff Garzik 		pegasus->rx_pool[i] = dev_alloc_skb(PEGASUS_MTU + 2);
572*5b2fc499SJeff Garzik 		/*
573*5b2fc499SJeff Garzik 		 ** we give up if the allocation fail. the tasklet will be
574*5b2fc499SJeff Garzik 		 ** rescheduled again anyway...
575*5b2fc499SJeff Garzik 		 */
576*5b2fc499SJeff Garzik 		if (pegasus->rx_pool[i] == NULL)
577*5b2fc499SJeff Garzik 			return;
578*5b2fc499SJeff Garzik 		skb_reserve(pegasus->rx_pool[i], 2);
579*5b2fc499SJeff Garzik 	}
580*5b2fc499SJeff Garzik }
581*5b2fc499SJeff Garzik 
582*5b2fc499SJeff Garzik static void free_skb_pool(pegasus_t * pegasus)
583*5b2fc499SJeff Garzik {
584*5b2fc499SJeff Garzik 	int i;
585*5b2fc499SJeff Garzik 
586*5b2fc499SJeff Garzik 	for (i = 0; i < RX_SKBS; i++) {
587*5b2fc499SJeff Garzik 		if (pegasus->rx_pool[i]) {
588*5b2fc499SJeff Garzik 			dev_kfree_skb(pegasus->rx_pool[i]);
589*5b2fc499SJeff Garzik 			pegasus->rx_pool[i] = NULL;
590*5b2fc499SJeff Garzik 		}
591*5b2fc499SJeff Garzik 	}
592*5b2fc499SJeff Garzik }
593*5b2fc499SJeff Garzik 
594*5b2fc499SJeff Garzik static inline struct sk_buff *pull_skb(pegasus_t * pegasus)
595*5b2fc499SJeff Garzik {
596*5b2fc499SJeff Garzik 	int i;
597*5b2fc499SJeff Garzik 	struct sk_buff *skb;
598*5b2fc499SJeff Garzik 
599*5b2fc499SJeff Garzik 	for (i = 0; i < RX_SKBS; i++) {
600*5b2fc499SJeff Garzik 		if (likely(pegasus->rx_pool[i] != NULL)) {
601*5b2fc499SJeff Garzik 			skb = pegasus->rx_pool[i];
602*5b2fc499SJeff Garzik 			pegasus->rx_pool[i] = NULL;
603*5b2fc499SJeff Garzik 			return skb;
604*5b2fc499SJeff Garzik 		}
605*5b2fc499SJeff Garzik 	}
606*5b2fc499SJeff Garzik 	return NULL;
607*5b2fc499SJeff Garzik }
608*5b2fc499SJeff Garzik 
609*5b2fc499SJeff Garzik static void read_bulk_callback(struct urb *urb)
610*5b2fc499SJeff Garzik {
611*5b2fc499SJeff Garzik 	pegasus_t *pegasus = urb->context;
612*5b2fc499SJeff Garzik 	struct net_device *net;
613*5b2fc499SJeff Garzik 	int rx_status, count = urb->actual_length;
614*5b2fc499SJeff Garzik 	u8 *buf = urb->transfer_buffer;
615*5b2fc499SJeff Garzik 	__u16 pkt_len;
616*5b2fc499SJeff Garzik 
617*5b2fc499SJeff Garzik 	if (!pegasus)
618*5b2fc499SJeff Garzik 		return;
619*5b2fc499SJeff Garzik 
620*5b2fc499SJeff Garzik 	net = pegasus->net;
621*5b2fc499SJeff Garzik 	if (!netif_device_present(net) || !netif_running(net))
622*5b2fc499SJeff Garzik 		return;
623*5b2fc499SJeff Garzik 
624*5b2fc499SJeff Garzik 	switch (urb->status) {
625*5b2fc499SJeff Garzik 	case 0:
626*5b2fc499SJeff Garzik 		break;
627*5b2fc499SJeff Garzik 	case -ETIME:
628*5b2fc499SJeff Garzik 		if (netif_msg_rx_err(pegasus))
629*5b2fc499SJeff Garzik 			pr_debug("%s: reset MAC\n", net->name);
630*5b2fc499SJeff Garzik 		pegasus->flags &= ~PEGASUS_RX_BUSY;
631*5b2fc499SJeff Garzik 		break;
632*5b2fc499SJeff Garzik 	case -EPIPE:		/* stall, or disconnect from TT */
633*5b2fc499SJeff Garzik 		/* FIXME schedule work to clear the halt */
634*5b2fc499SJeff Garzik 		if (netif_msg_rx_err(pegasus))
635*5b2fc499SJeff Garzik 			printk(KERN_WARNING "%s: no rx stall recovery\n",
636*5b2fc499SJeff Garzik 					net->name);
637*5b2fc499SJeff Garzik 		return;
638*5b2fc499SJeff Garzik 	case -ENOENT:
639*5b2fc499SJeff Garzik 	case -ECONNRESET:
640*5b2fc499SJeff Garzik 	case -ESHUTDOWN:
641*5b2fc499SJeff Garzik 		if (netif_msg_ifdown(pegasus))
642*5b2fc499SJeff Garzik 			pr_debug("%s: rx unlink, %d\n", net->name, urb->status);
643*5b2fc499SJeff Garzik 		return;
644*5b2fc499SJeff Garzik 	default:
645*5b2fc499SJeff Garzik 		if (netif_msg_rx_err(pegasus))
646*5b2fc499SJeff Garzik 			pr_debug("%s: RX status %d\n", net->name, urb->status);
647*5b2fc499SJeff Garzik 		goto goon;
648*5b2fc499SJeff Garzik 	}
649*5b2fc499SJeff Garzik 
650*5b2fc499SJeff Garzik 	if (!count || count < 4)
651*5b2fc499SJeff Garzik 		goto goon;
652*5b2fc499SJeff Garzik 
653*5b2fc499SJeff Garzik 	rx_status = buf[count - 2];
654*5b2fc499SJeff Garzik 	if (rx_status & 0x1e) {
655*5b2fc499SJeff Garzik 		if (netif_msg_rx_err(pegasus))
656*5b2fc499SJeff Garzik 			pr_debug("%s: RX packet error %x\n",
657*5b2fc499SJeff Garzik 					net->name, rx_status);
658*5b2fc499SJeff Garzik 		pegasus->stats.rx_errors++;
659*5b2fc499SJeff Garzik 		if (rx_status & 0x06)	// long or runt
660*5b2fc499SJeff Garzik 			pegasus->stats.rx_length_errors++;
661*5b2fc499SJeff Garzik 		if (rx_status & 0x08)
662*5b2fc499SJeff Garzik 			pegasus->stats.rx_crc_errors++;
663*5b2fc499SJeff Garzik 		if (rx_status & 0x10)	// extra bits
664*5b2fc499SJeff Garzik 			pegasus->stats.rx_frame_errors++;
665*5b2fc499SJeff Garzik 		goto goon;
666*5b2fc499SJeff Garzik 	}
667*5b2fc499SJeff Garzik 	if (pegasus->chip == 0x8513) {
668*5b2fc499SJeff Garzik 		pkt_len = le32_to_cpu(*(__le32 *)urb->transfer_buffer);
669*5b2fc499SJeff Garzik 		pkt_len &= 0x0fff;
670*5b2fc499SJeff Garzik 		pegasus->rx_skb->data += 2;
671*5b2fc499SJeff Garzik 	} else {
672*5b2fc499SJeff Garzik 		pkt_len = buf[count - 3] << 8;
673*5b2fc499SJeff Garzik 		pkt_len += buf[count - 4];
674*5b2fc499SJeff Garzik 		pkt_len &= 0xfff;
675*5b2fc499SJeff Garzik 		pkt_len -= 8;
676*5b2fc499SJeff Garzik 	}
677*5b2fc499SJeff Garzik 
678*5b2fc499SJeff Garzik 	/*
679*5b2fc499SJeff Garzik 	 * If the packet is unreasonably long, quietly drop it rather than
680*5b2fc499SJeff Garzik 	 * kernel panicing by calling skb_put.
681*5b2fc499SJeff Garzik 	 */
682*5b2fc499SJeff Garzik 	if (pkt_len > PEGASUS_MTU)
683*5b2fc499SJeff Garzik 		goto goon;
684*5b2fc499SJeff Garzik 
685*5b2fc499SJeff Garzik 	/*
686*5b2fc499SJeff Garzik 	 * at this point we are sure pegasus->rx_skb != NULL
687*5b2fc499SJeff Garzik 	 * so we go ahead and pass up the packet.
688*5b2fc499SJeff Garzik 	 */
689*5b2fc499SJeff Garzik 	skb_put(pegasus->rx_skb, pkt_len);
690*5b2fc499SJeff Garzik 	pegasus->rx_skb->protocol = eth_type_trans(pegasus->rx_skb, net);
691*5b2fc499SJeff Garzik 	netif_rx(pegasus->rx_skb);
692*5b2fc499SJeff Garzik 	pegasus->stats.rx_packets++;
693*5b2fc499SJeff Garzik 	pegasus->stats.rx_bytes += pkt_len;
694*5b2fc499SJeff Garzik 
695*5b2fc499SJeff Garzik 	if (pegasus->flags & PEGASUS_UNPLUG)
696*5b2fc499SJeff Garzik 		return;
697*5b2fc499SJeff Garzik 
698*5b2fc499SJeff Garzik 	spin_lock(&pegasus->rx_pool_lock);
699*5b2fc499SJeff Garzik 	pegasus->rx_skb = pull_skb(pegasus);
700*5b2fc499SJeff Garzik 	spin_unlock(&pegasus->rx_pool_lock);
701*5b2fc499SJeff Garzik 
702*5b2fc499SJeff Garzik 	if (pegasus->rx_skb == NULL)
703*5b2fc499SJeff Garzik 		goto tl_sched;
704*5b2fc499SJeff Garzik goon:
705*5b2fc499SJeff Garzik 	usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb,
706*5b2fc499SJeff Garzik 			  usb_rcvbulkpipe(pegasus->usb, 1),
707*5b2fc499SJeff Garzik 			  pegasus->rx_skb->data, PEGASUS_MTU + 8,
708*5b2fc499SJeff Garzik 			  read_bulk_callback, pegasus);
709*5b2fc499SJeff Garzik 	rx_status = usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC);
710*5b2fc499SJeff Garzik 	if (rx_status == -ENODEV)
711*5b2fc499SJeff Garzik 		netif_device_detach(pegasus->net);
712*5b2fc499SJeff Garzik 	else if (rx_status) {
713*5b2fc499SJeff Garzik 		pegasus->flags |= PEGASUS_RX_URB_FAIL;
714*5b2fc499SJeff Garzik 		goto tl_sched;
715*5b2fc499SJeff Garzik 	} else {
716*5b2fc499SJeff Garzik 		pegasus->flags &= ~PEGASUS_RX_URB_FAIL;
717*5b2fc499SJeff Garzik 	}
718*5b2fc499SJeff Garzik 
719*5b2fc499SJeff Garzik 	return;
720*5b2fc499SJeff Garzik 
721*5b2fc499SJeff Garzik tl_sched:
722*5b2fc499SJeff Garzik 	tasklet_schedule(&pegasus->rx_tl);
723*5b2fc499SJeff Garzik }
724*5b2fc499SJeff Garzik 
725*5b2fc499SJeff Garzik static void rx_fixup(unsigned long data)
726*5b2fc499SJeff Garzik {
727*5b2fc499SJeff Garzik 	pegasus_t *pegasus;
728*5b2fc499SJeff Garzik 	unsigned long flags;
729*5b2fc499SJeff Garzik 	int status;
730*5b2fc499SJeff Garzik 
731*5b2fc499SJeff Garzik 	pegasus = (pegasus_t *) data;
732*5b2fc499SJeff Garzik 	if (pegasus->flags & PEGASUS_UNPLUG)
733*5b2fc499SJeff Garzik 		return;
734*5b2fc499SJeff Garzik 
735*5b2fc499SJeff Garzik 	spin_lock_irqsave(&pegasus->rx_pool_lock, flags);
736*5b2fc499SJeff Garzik 	fill_skb_pool(pegasus);
737*5b2fc499SJeff Garzik 	if (pegasus->flags & PEGASUS_RX_URB_FAIL)
738*5b2fc499SJeff Garzik 		if (pegasus->rx_skb)
739*5b2fc499SJeff Garzik 			goto try_again;
740*5b2fc499SJeff Garzik 	if (pegasus->rx_skb == NULL) {
741*5b2fc499SJeff Garzik 		pegasus->rx_skb = pull_skb(pegasus);
742*5b2fc499SJeff Garzik 	}
743*5b2fc499SJeff Garzik 	if (pegasus->rx_skb == NULL) {
744*5b2fc499SJeff Garzik 		if (netif_msg_rx_err(pegasus))
745*5b2fc499SJeff Garzik 			printk(KERN_WARNING "%s: low on memory\n",
746*5b2fc499SJeff Garzik 					pegasus->net->name);
747*5b2fc499SJeff Garzik 		tasklet_schedule(&pegasus->rx_tl);
748*5b2fc499SJeff Garzik 		goto done;
749*5b2fc499SJeff Garzik 	}
750*5b2fc499SJeff Garzik 	usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb,
751*5b2fc499SJeff Garzik 			  usb_rcvbulkpipe(pegasus->usb, 1),
752*5b2fc499SJeff Garzik 			  pegasus->rx_skb->data, PEGASUS_MTU + 8,
753*5b2fc499SJeff Garzik 			  read_bulk_callback, pegasus);
754*5b2fc499SJeff Garzik try_again:
755*5b2fc499SJeff Garzik 	status = usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC);
756*5b2fc499SJeff Garzik 	if (status == -ENODEV)
757*5b2fc499SJeff Garzik 		netif_device_detach(pegasus->net);
758*5b2fc499SJeff Garzik 	else if (status) {
759*5b2fc499SJeff Garzik 		pegasus->flags |= PEGASUS_RX_URB_FAIL;
760*5b2fc499SJeff Garzik 		tasklet_schedule(&pegasus->rx_tl);
761*5b2fc499SJeff Garzik 	} else {
762*5b2fc499SJeff Garzik 		pegasus->flags &= ~PEGASUS_RX_URB_FAIL;
763*5b2fc499SJeff Garzik 	}
764*5b2fc499SJeff Garzik done:
765*5b2fc499SJeff Garzik 	spin_unlock_irqrestore(&pegasus->rx_pool_lock, flags);
766*5b2fc499SJeff Garzik }
767*5b2fc499SJeff Garzik 
768*5b2fc499SJeff Garzik static void write_bulk_callback(struct urb *urb)
769*5b2fc499SJeff Garzik {
770*5b2fc499SJeff Garzik 	pegasus_t *pegasus = urb->context;
771*5b2fc499SJeff Garzik 	struct net_device *net = pegasus->net;
772*5b2fc499SJeff Garzik 
773*5b2fc499SJeff Garzik 	if (!pegasus)
774*5b2fc499SJeff Garzik 		return;
775*5b2fc499SJeff Garzik 
776*5b2fc499SJeff Garzik 	if (!netif_device_present(net) || !netif_running(net))
777*5b2fc499SJeff Garzik 		return;
778*5b2fc499SJeff Garzik 
779*5b2fc499SJeff Garzik 	switch (urb->status) {
780*5b2fc499SJeff Garzik 	case -EPIPE:
781*5b2fc499SJeff Garzik 		/* FIXME schedule_work() to clear the tx halt */
782*5b2fc499SJeff Garzik 		netif_stop_queue(net);
783*5b2fc499SJeff Garzik 		if (netif_msg_tx_err(pegasus))
784*5b2fc499SJeff Garzik 			printk(KERN_WARNING "%s: no tx stall recovery\n",
785*5b2fc499SJeff Garzik 					net->name);
786*5b2fc499SJeff Garzik 		return;
787*5b2fc499SJeff Garzik 	case -ENOENT:
788*5b2fc499SJeff Garzik 	case -ECONNRESET:
789*5b2fc499SJeff Garzik 	case -ESHUTDOWN:
790*5b2fc499SJeff Garzik 		if (netif_msg_ifdown(pegasus))
791*5b2fc499SJeff Garzik 			pr_debug("%s: tx unlink, %d\n", net->name, urb->status);
792*5b2fc499SJeff Garzik 		return;
793*5b2fc499SJeff Garzik 	default:
794*5b2fc499SJeff Garzik 		if (netif_msg_tx_err(pegasus))
795*5b2fc499SJeff Garzik 			pr_info("%s: TX status %d\n", net->name, urb->status);
796*5b2fc499SJeff Garzik 		/* FALL THROUGH */
797*5b2fc499SJeff Garzik 	case 0:
798*5b2fc499SJeff Garzik 		break;
799*5b2fc499SJeff Garzik 	}
800*5b2fc499SJeff Garzik 
801*5b2fc499SJeff Garzik 	net->trans_start = jiffies;
802*5b2fc499SJeff Garzik 	netif_wake_queue(net);
803*5b2fc499SJeff Garzik }
804*5b2fc499SJeff Garzik 
805*5b2fc499SJeff Garzik static void intr_callback(struct urb *urb)
806*5b2fc499SJeff Garzik {
807*5b2fc499SJeff Garzik 	pegasus_t *pegasus = urb->context;
808*5b2fc499SJeff Garzik 	struct net_device *net;
809*5b2fc499SJeff Garzik 	int status;
810*5b2fc499SJeff Garzik 
811*5b2fc499SJeff Garzik 	if (!pegasus)
812*5b2fc499SJeff Garzik 		return;
813*5b2fc499SJeff Garzik 	net = pegasus->net;
814*5b2fc499SJeff Garzik 
815*5b2fc499SJeff Garzik 	switch (urb->status) {
816*5b2fc499SJeff Garzik 	case 0:
817*5b2fc499SJeff Garzik 		break;
818*5b2fc499SJeff Garzik 	case -ECONNRESET:	/* unlink */
819*5b2fc499SJeff Garzik 	case -ENOENT:
820*5b2fc499SJeff Garzik 	case -ESHUTDOWN:
821*5b2fc499SJeff Garzik 		return;
822*5b2fc499SJeff Garzik 	default:
823*5b2fc499SJeff Garzik 		/* some Pegasus-I products report LOTS of data
824*5b2fc499SJeff Garzik 		 * toggle errors... avoid log spamming
825*5b2fc499SJeff Garzik 		 */
826*5b2fc499SJeff Garzik 		if (netif_msg_timer(pegasus))
827*5b2fc499SJeff Garzik 			pr_debug("%s: intr status %d\n", net->name,
828*5b2fc499SJeff Garzik 					urb->status);
829*5b2fc499SJeff Garzik 	}
830*5b2fc499SJeff Garzik 
831*5b2fc499SJeff Garzik 	if (urb->actual_length >= 6) {
832*5b2fc499SJeff Garzik 		u8	* d = urb->transfer_buffer;
833*5b2fc499SJeff Garzik 
834*5b2fc499SJeff Garzik 		/* byte 0 == tx_status1, reg 2B */
835*5b2fc499SJeff Garzik 		if (d[0] & (TX_UNDERRUN|EXCESSIVE_COL
836*5b2fc499SJeff Garzik 					|LATE_COL|JABBER_TIMEOUT)) {
837*5b2fc499SJeff Garzik 			pegasus->stats.tx_errors++;
838*5b2fc499SJeff Garzik 			if (d[0] & TX_UNDERRUN)
839*5b2fc499SJeff Garzik 				pegasus->stats.tx_fifo_errors++;
840*5b2fc499SJeff Garzik 			if (d[0] & (EXCESSIVE_COL | JABBER_TIMEOUT))
841*5b2fc499SJeff Garzik 				pegasus->stats.tx_aborted_errors++;
842*5b2fc499SJeff Garzik 			if (d[0] & LATE_COL)
843*5b2fc499SJeff Garzik 				pegasus->stats.tx_window_errors++;
844*5b2fc499SJeff Garzik 		}
845*5b2fc499SJeff Garzik 
846*5b2fc499SJeff Garzik 		/* d[5].LINK_STATUS lies on some adapters.
847*5b2fc499SJeff Garzik 		 * d[0].NO_CARRIER kicks in only with failed TX.
848*5b2fc499SJeff Garzik 		 * ... so monitoring with MII may be safest.
849*5b2fc499SJeff Garzik 		 */
850*5b2fc499SJeff Garzik 
851*5b2fc499SJeff Garzik 		/* bytes 3-4 == rx_lostpkt, reg 2E/2F */
852*5b2fc499SJeff Garzik 		pegasus->stats.rx_missed_errors += ((d[3] & 0x7f) << 8) | d[4];
853*5b2fc499SJeff Garzik 	}
854*5b2fc499SJeff Garzik 
855*5b2fc499SJeff Garzik 	status = usb_submit_urb(urb, GFP_ATOMIC);
856*5b2fc499SJeff Garzik 	if (status == -ENODEV)
857*5b2fc499SJeff Garzik 		netif_device_detach(pegasus->net);
858*5b2fc499SJeff Garzik 	if (status && netif_msg_timer(pegasus))
859*5b2fc499SJeff Garzik 		printk(KERN_ERR "%s: can't resubmit interrupt urb, %d\n",
860*5b2fc499SJeff Garzik 				net->name, status);
861*5b2fc499SJeff Garzik }
862*5b2fc499SJeff Garzik 
863*5b2fc499SJeff Garzik static void pegasus_tx_timeout(struct net_device *net)
864*5b2fc499SJeff Garzik {
865*5b2fc499SJeff Garzik 	pegasus_t *pegasus = netdev_priv(net);
866*5b2fc499SJeff Garzik 	if (netif_msg_timer(pegasus))
867*5b2fc499SJeff Garzik 		printk(KERN_WARNING "%s: tx timeout\n", net->name);
868*5b2fc499SJeff Garzik 	usb_unlink_urb(pegasus->tx_urb);
869*5b2fc499SJeff Garzik 	pegasus->stats.tx_errors++;
870*5b2fc499SJeff Garzik }
871*5b2fc499SJeff Garzik 
872*5b2fc499SJeff Garzik static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net)
873*5b2fc499SJeff Garzik {
874*5b2fc499SJeff Garzik 	pegasus_t *pegasus = netdev_priv(net);
875*5b2fc499SJeff Garzik 	int count = ((skb->len + 2) & 0x3f) ? skb->len + 2 : skb->len + 3;
876*5b2fc499SJeff Garzik 	int res;
877*5b2fc499SJeff Garzik 	__u16 l16 = skb->len;
878*5b2fc499SJeff Garzik 
879*5b2fc499SJeff Garzik 	netif_stop_queue(net);
880*5b2fc499SJeff Garzik 
881*5b2fc499SJeff Garzik 	((__le16 *) pegasus->tx_buff)[0] = cpu_to_le16(l16);
882*5b2fc499SJeff Garzik 	skb_copy_from_linear_data(skb, pegasus->tx_buff + 2, skb->len);
883*5b2fc499SJeff Garzik 	usb_fill_bulk_urb(pegasus->tx_urb, pegasus->usb,
884*5b2fc499SJeff Garzik 			  usb_sndbulkpipe(pegasus->usb, 2),
885*5b2fc499SJeff Garzik 			  pegasus->tx_buff, count,
886*5b2fc499SJeff Garzik 			  write_bulk_callback, pegasus);
887*5b2fc499SJeff Garzik 	if ((res = usb_submit_urb(pegasus->tx_urb, GFP_ATOMIC))) {
888*5b2fc499SJeff Garzik 		if (netif_msg_tx_err(pegasus))
889*5b2fc499SJeff Garzik 			printk(KERN_WARNING "%s: fail tx, %d\n",
890*5b2fc499SJeff Garzik 					net->name, res);
891*5b2fc499SJeff Garzik 		switch (res) {
892*5b2fc499SJeff Garzik 		case -EPIPE:		/* stall, or disconnect from TT */
893*5b2fc499SJeff Garzik 			/* cleanup should already have been scheduled */
894*5b2fc499SJeff Garzik 			break;
895*5b2fc499SJeff Garzik 		case -ENODEV:		/* disconnect() upcoming */
896*5b2fc499SJeff Garzik 			netif_device_detach(pegasus->net);
897*5b2fc499SJeff Garzik 			break;
898*5b2fc499SJeff Garzik 		default:
899*5b2fc499SJeff Garzik 			pegasus->stats.tx_errors++;
900*5b2fc499SJeff Garzik 			netif_start_queue(net);
901*5b2fc499SJeff Garzik 		}
902*5b2fc499SJeff Garzik 	} else {
903*5b2fc499SJeff Garzik 		pegasus->stats.tx_packets++;
904*5b2fc499SJeff Garzik 		pegasus->stats.tx_bytes += skb->len;
905*5b2fc499SJeff Garzik 		net->trans_start = jiffies;
906*5b2fc499SJeff Garzik 	}
907*5b2fc499SJeff Garzik 	dev_kfree_skb(skb);
908*5b2fc499SJeff Garzik 
909*5b2fc499SJeff Garzik 	return 0;
910*5b2fc499SJeff Garzik }
911*5b2fc499SJeff Garzik 
912*5b2fc499SJeff Garzik static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev)
913*5b2fc499SJeff Garzik {
914*5b2fc499SJeff Garzik 	return &((pegasus_t *) netdev_priv(dev))->stats;
915*5b2fc499SJeff Garzik }
916*5b2fc499SJeff Garzik 
917*5b2fc499SJeff Garzik static inline void disable_net_traffic(pegasus_t * pegasus)
918*5b2fc499SJeff Garzik {
919*5b2fc499SJeff Garzik 	int tmp = 0;
920*5b2fc499SJeff Garzik 
921*5b2fc499SJeff Garzik 	set_registers(pegasus, EthCtrl0, 2, &tmp);
922*5b2fc499SJeff Garzik }
923*5b2fc499SJeff Garzik 
924*5b2fc499SJeff Garzik static inline void get_interrupt_interval(pegasus_t * pegasus)
925*5b2fc499SJeff Garzik {
926*5b2fc499SJeff Garzik 	__u8 data[2];
927*5b2fc499SJeff Garzik 
928*5b2fc499SJeff Garzik 	read_eprom_word(pegasus, 4, (__u16 *) data);
929*5b2fc499SJeff Garzik 	if (pegasus->usb->speed != USB_SPEED_HIGH) {
930*5b2fc499SJeff Garzik 		if (data[1] < 0x80) {
931*5b2fc499SJeff Garzik 			if (netif_msg_timer(pegasus))
932*5b2fc499SJeff Garzik 				dev_info(&pegasus->intf->dev, "intr interval "
933*5b2fc499SJeff Garzik 					"changed from %ums to %ums\n",
934*5b2fc499SJeff Garzik 					data[1], 0x80);
935*5b2fc499SJeff Garzik 			data[1] = 0x80;
936*5b2fc499SJeff Garzik #ifdef PEGASUS_WRITE_EEPROM
937*5b2fc499SJeff Garzik 			write_eprom_word(pegasus, 4, *(__u16 *) data);
938*5b2fc499SJeff Garzik #endif
939*5b2fc499SJeff Garzik 		}
940*5b2fc499SJeff Garzik 	}
941*5b2fc499SJeff Garzik 	pegasus->intr_interval = data[1];
942*5b2fc499SJeff Garzik }
943*5b2fc499SJeff Garzik 
944*5b2fc499SJeff Garzik static void set_carrier(struct net_device *net)
945*5b2fc499SJeff Garzik {
946*5b2fc499SJeff Garzik 	pegasus_t *pegasus = netdev_priv(net);
947*5b2fc499SJeff Garzik 	u16 tmp;
948*5b2fc499SJeff Garzik 
949*5b2fc499SJeff Garzik 	if (read_mii_word(pegasus, pegasus->phy, MII_BMSR, &tmp))
950*5b2fc499SJeff Garzik 		return;
951*5b2fc499SJeff Garzik 
952*5b2fc499SJeff Garzik 	if (tmp & BMSR_LSTATUS)
953*5b2fc499SJeff Garzik 		netif_carrier_on(net);
954*5b2fc499SJeff Garzik 	else
955*5b2fc499SJeff Garzik 		netif_carrier_off(net);
956*5b2fc499SJeff Garzik }
957*5b2fc499SJeff Garzik 
958*5b2fc499SJeff Garzik static void free_all_urbs(pegasus_t * pegasus)
959*5b2fc499SJeff Garzik {
960*5b2fc499SJeff Garzik 	usb_free_urb(pegasus->intr_urb);
961*5b2fc499SJeff Garzik 	usb_free_urb(pegasus->tx_urb);
962*5b2fc499SJeff Garzik 	usb_free_urb(pegasus->rx_urb);
963*5b2fc499SJeff Garzik 	usb_free_urb(pegasus->ctrl_urb);
964*5b2fc499SJeff Garzik }
965*5b2fc499SJeff Garzik 
966*5b2fc499SJeff Garzik static void unlink_all_urbs(pegasus_t * pegasus)
967*5b2fc499SJeff Garzik {
968*5b2fc499SJeff Garzik 	usb_kill_urb(pegasus->intr_urb);
969*5b2fc499SJeff Garzik 	usb_kill_urb(pegasus->tx_urb);
970*5b2fc499SJeff Garzik 	usb_kill_urb(pegasus->rx_urb);
971*5b2fc499SJeff Garzik 	usb_kill_urb(pegasus->ctrl_urb);
972*5b2fc499SJeff Garzik }
973*5b2fc499SJeff Garzik 
974*5b2fc499SJeff Garzik static int alloc_urbs(pegasus_t * pegasus)
975*5b2fc499SJeff Garzik {
976*5b2fc499SJeff Garzik 	pegasus->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL);
977*5b2fc499SJeff Garzik 	if (!pegasus->ctrl_urb) {
978*5b2fc499SJeff Garzik 		return 0;
979*5b2fc499SJeff Garzik 	}
980*5b2fc499SJeff Garzik 	pegasus->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
981*5b2fc499SJeff Garzik 	if (!pegasus->rx_urb) {
982*5b2fc499SJeff Garzik 		usb_free_urb(pegasus->ctrl_urb);
983*5b2fc499SJeff Garzik 		return 0;
984*5b2fc499SJeff Garzik 	}
985*5b2fc499SJeff Garzik 	pegasus->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
986*5b2fc499SJeff Garzik 	if (!pegasus->tx_urb) {
987*5b2fc499SJeff Garzik 		usb_free_urb(pegasus->rx_urb);
988*5b2fc499SJeff Garzik 		usb_free_urb(pegasus->ctrl_urb);
989*5b2fc499SJeff Garzik 		return 0;
990*5b2fc499SJeff Garzik 	}
991*5b2fc499SJeff Garzik 	pegasus->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
992*5b2fc499SJeff Garzik 	if (!pegasus->intr_urb) {
993*5b2fc499SJeff Garzik 		usb_free_urb(pegasus->tx_urb);
994*5b2fc499SJeff Garzik 		usb_free_urb(pegasus->rx_urb);
995*5b2fc499SJeff Garzik 		usb_free_urb(pegasus->ctrl_urb);
996*5b2fc499SJeff Garzik 		return 0;
997*5b2fc499SJeff Garzik 	}
998*5b2fc499SJeff Garzik 
999*5b2fc499SJeff Garzik 	return 1;
1000*5b2fc499SJeff Garzik }
1001*5b2fc499SJeff Garzik 
1002*5b2fc499SJeff Garzik static int pegasus_open(struct net_device *net)
1003*5b2fc499SJeff Garzik {
1004*5b2fc499SJeff Garzik 	pegasus_t *pegasus = netdev_priv(net);
1005*5b2fc499SJeff Garzik 	int res;
1006*5b2fc499SJeff Garzik 
1007*5b2fc499SJeff Garzik 	if (pegasus->rx_skb == NULL)
1008*5b2fc499SJeff Garzik 		pegasus->rx_skb = pull_skb(pegasus);
1009*5b2fc499SJeff Garzik 	/*
1010*5b2fc499SJeff Garzik 	 ** Note: no point to free the pool.  it is empty :-)
1011*5b2fc499SJeff Garzik 	 */
1012*5b2fc499SJeff Garzik 	if (!pegasus->rx_skb)
1013*5b2fc499SJeff Garzik 		return -ENOMEM;
1014*5b2fc499SJeff Garzik 
1015*5b2fc499SJeff Garzik 	res = set_registers(pegasus, EthID, 6, net->dev_addr);
1016*5b2fc499SJeff Garzik 
1017*5b2fc499SJeff Garzik 	usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb,
1018*5b2fc499SJeff Garzik 			  usb_rcvbulkpipe(pegasus->usb, 1),
1019*5b2fc499SJeff Garzik 			  pegasus->rx_skb->data, PEGASUS_MTU + 8,
1020*5b2fc499SJeff Garzik 			  read_bulk_callback, pegasus);
1021*5b2fc499SJeff Garzik 	if ((res = usb_submit_urb(pegasus->rx_urb, GFP_KERNEL))) {
1022*5b2fc499SJeff Garzik 		if (res == -ENODEV)
1023*5b2fc499SJeff Garzik 			netif_device_detach(pegasus->net);
1024*5b2fc499SJeff Garzik 		if (netif_msg_ifup(pegasus))
1025*5b2fc499SJeff Garzik 			pr_debug("%s: failed rx_urb, %d", net->name, res);
1026*5b2fc499SJeff Garzik 		goto exit;
1027*5b2fc499SJeff Garzik 	}
1028*5b2fc499SJeff Garzik 
1029*5b2fc499SJeff Garzik 	usb_fill_int_urb(pegasus->intr_urb, pegasus->usb,
1030*5b2fc499SJeff Garzik 			 usb_rcvintpipe(pegasus->usb, 3),
1031*5b2fc499SJeff Garzik 			 pegasus->intr_buff, sizeof (pegasus->intr_buff),
1032*5b2fc499SJeff Garzik 			 intr_callback, pegasus, pegasus->intr_interval);
1033*5b2fc499SJeff Garzik 	if ((res = usb_submit_urb(pegasus->intr_urb, GFP_KERNEL))) {
1034*5b2fc499SJeff Garzik 		if (res == -ENODEV)
1035*5b2fc499SJeff Garzik 			netif_device_detach(pegasus->net);
1036*5b2fc499SJeff Garzik 		if (netif_msg_ifup(pegasus))
1037*5b2fc499SJeff Garzik 			pr_debug("%s: failed intr_urb, %d\n", net->name, res);
1038*5b2fc499SJeff Garzik 		usb_kill_urb(pegasus->rx_urb);
1039*5b2fc499SJeff Garzik 		goto exit;
1040*5b2fc499SJeff Garzik 	}
1041*5b2fc499SJeff Garzik 	if ((res = enable_net_traffic(net, pegasus->usb))) {
1042*5b2fc499SJeff Garzik 		if (netif_msg_ifup(pegasus))
1043*5b2fc499SJeff Garzik 			pr_debug("%s: can't enable_net_traffic() - %d\n",
1044*5b2fc499SJeff Garzik 					net->name, res);
1045*5b2fc499SJeff Garzik 		res = -EIO;
1046*5b2fc499SJeff Garzik 		usb_kill_urb(pegasus->rx_urb);
1047*5b2fc499SJeff Garzik 		usb_kill_urb(pegasus->intr_urb);
1048*5b2fc499SJeff Garzik 		free_skb_pool(pegasus);
1049*5b2fc499SJeff Garzik 		goto exit;
1050*5b2fc499SJeff Garzik 	}
1051*5b2fc499SJeff Garzik 	set_carrier(net);
1052*5b2fc499SJeff Garzik 	netif_start_queue(net);
1053*5b2fc499SJeff Garzik 	if (netif_msg_ifup(pegasus))
1054*5b2fc499SJeff Garzik 		pr_debug("%s: open\n", net->name);
1055*5b2fc499SJeff Garzik 	res = 0;
1056*5b2fc499SJeff Garzik exit:
1057*5b2fc499SJeff Garzik 	return res;
1058*5b2fc499SJeff Garzik }
1059*5b2fc499SJeff Garzik 
1060*5b2fc499SJeff Garzik static int pegasus_close(struct net_device *net)
1061*5b2fc499SJeff Garzik {
1062*5b2fc499SJeff Garzik 	pegasus_t *pegasus = netdev_priv(net);
1063*5b2fc499SJeff Garzik 
1064*5b2fc499SJeff Garzik 	netif_stop_queue(net);
1065*5b2fc499SJeff Garzik 	if (!(pegasus->flags & PEGASUS_UNPLUG))
1066*5b2fc499SJeff Garzik 		disable_net_traffic(pegasus);
1067*5b2fc499SJeff Garzik 	tasklet_kill(&pegasus->rx_tl);
1068*5b2fc499SJeff Garzik 	unlink_all_urbs(pegasus);
1069*5b2fc499SJeff Garzik 
1070*5b2fc499SJeff Garzik 	return 0;
1071*5b2fc499SJeff Garzik }
1072*5b2fc499SJeff Garzik 
1073*5b2fc499SJeff Garzik static void pegasus_get_drvinfo(struct net_device *dev,
1074*5b2fc499SJeff Garzik 				struct ethtool_drvinfo *info)
1075*5b2fc499SJeff Garzik {
1076*5b2fc499SJeff Garzik 	pegasus_t *pegasus = netdev_priv(dev);
1077*5b2fc499SJeff Garzik 	strncpy(info->driver, driver_name, sizeof (info->driver) - 1);
1078*5b2fc499SJeff Garzik 	strncpy(info->version, DRIVER_VERSION, sizeof (info->version) - 1);
1079*5b2fc499SJeff Garzik 	usb_make_path(pegasus->usb, info->bus_info, sizeof (info->bus_info));
1080*5b2fc499SJeff Garzik }
1081*5b2fc499SJeff Garzik 
1082*5b2fc499SJeff Garzik /* also handles three patterns of some kind in hardware */
1083*5b2fc499SJeff Garzik #define	WOL_SUPPORTED	(WAKE_MAGIC|WAKE_PHY)
1084*5b2fc499SJeff Garzik 
1085*5b2fc499SJeff Garzik static void
1086*5b2fc499SJeff Garzik pegasus_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
1087*5b2fc499SJeff Garzik {
1088*5b2fc499SJeff Garzik 	pegasus_t	*pegasus = netdev_priv(dev);
1089*5b2fc499SJeff Garzik 
1090*5b2fc499SJeff Garzik 	wol->supported = WAKE_MAGIC | WAKE_PHY;
1091*5b2fc499SJeff Garzik 	wol->wolopts = pegasus->wolopts;
1092*5b2fc499SJeff Garzik }
1093*5b2fc499SJeff Garzik 
1094*5b2fc499SJeff Garzik static int
1095*5b2fc499SJeff Garzik pegasus_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
1096*5b2fc499SJeff Garzik {
1097*5b2fc499SJeff Garzik 	pegasus_t	*pegasus = netdev_priv(dev);
1098*5b2fc499SJeff Garzik 	u8		reg78 = 0x04;
1099*5b2fc499SJeff Garzik 
1100*5b2fc499SJeff Garzik 	if (wol->wolopts & ~WOL_SUPPORTED)
1101*5b2fc499SJeff Garzik 		return -EINVAL;
1102*5b2fc499SJeff Garzik 
1103*5b2fc499SJeff Garzik 	if (wol->wolopts & WAKE_MAGIC)
1104*5b2fc499SJeff Garzik 		reg78 |= 0x80;
1105*5b2fc499SJeff Garzik 	if (wol->wolopts & WAKE_PHY)
1106*5b2fc499SJeff Garzik 		reg78 |= 0x40;
1107*5b2fc499SJeff Garzik 	/* FIXME this 0x10 bit still needs to get set in the chip... */
1108*5b2fc499SJeff Garzik 	if (wol->wolopts)
1109*5b2fc499SJeff Garzik 		pegasus->eth_regs[0] |= 0x10;
1110*5b2fc499SJeff Garzik 	else
1111*5b2fc499SJeff Garzik 		pegasus->eth_regs[0] &= ~0x10;
1112*5b2fc499SJeff Garzik 	pegasus->wolopts = wol->wolopts;
1113*5b2fc499SJeff Garzik 	return set_register(pegasus, WakeupControl, reg78);
1114*5b2fc499SJeff Garzik }
1115*5b2fc499SJeff Garzik 
1116*5b2fc499SJeff Garzik static inline void pegasus_reset_wol(struct net_device *dev)
1117*5b2fc499SJeff Garzik {
1118*5b2fc499SJeff Garzik 	struct ethtool_wolinfo wol;
1119*5b2fc499SJeff Garzik 
1120*5b2fc499SJeff Garzik 	memset(&wol, 0, sizeof wol);
1121*5b2fc499SJeff Garzik 	(void) pegasus_set_wol(dev, &wol);
1122*5b2fc499SJeff Garzik }
1123*5b2fc499SJeff Garzik 
1124*5b2fc499SJeff Garzik static int
1125*5b2fc499SJeff Garzik pegasus_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
1126*5b2fc499SJeff Garzik {
1127*5b2fc499SJeff Garzik 	pegasus_t *pegasus;
1128*5b2fc499SJeff Garzik 
1129*5b2fc499SJeff Garzik 	if (in_atomic())
1130*5b2fc499SJeff Garzik 		return 0;
1131*5b2fc499SJeff Garzik 
1132*5b2fc499SJeff Garzik 	pegasus = netdev_priv(dev);
1133*5b2fc499SJeff Garzik 	mii_ethtool_gset(&pegasus->mii, ecmd);
1134*5b2fc499SJeff Garzik 
1135*5b2fc499SJeff Garzik 	return 0;
1136*5b2fc499SJeff Garzik }
1137*5b2fc499SJeff Garzik 
1138*5b2fc499SJeff Garzik static int
1139*5b2fc499SJeff Garzik pegasus_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
1140*5b2fc499SJeff Garzik {
1141*5b2fc499SJeff Garzik 	pegasus_t *pegasus = netdev_priv(dev);
1142*5b2fc499SJeff Garzik 	return mii_ethtool_sset(&pegasus->mii, ecmd);
1143*5b2fc499SJeff Garzik }
1144*5b2fc499SJeff Garzik 
1145*5b2fc499SJeff Garzik static int pegasus_nway_reset(struct net_device *dev)
1146*5b2fc499SJeff Garzik {
1147*5b2fc499SJeff Garzik 	pegasus_t *pegasus = netdev_priv(dev);
1148*5b2fc499SJeff Garzik 	return mii_nway_restart(&pegasus->mii);
1149*5b2fc499SJeff Garzik }
1150*5b2fc499SJeff Garzik 
1151*5b2fc499SJeff Garzik static u32 pegasus_get_link(struct net_device *dev)
1152*5b2fc499SJeff Garzik {
1153*5b2fc499SJeff Garzik 	pegasus_t *pegasus = netdev_priv(dev);
1154*5b2fc499SJeff Garzik 	return mii_link_ok(&pegasus->mii);
1155*5b2fc499SJeff Garzik }
1156*5b2fc499SJeff Garzik 
1157*5b2fc499SJeff Garzik static u32 pegasus_get_msglevel(struct net_device *dev)
1158*5b2fc499SJeff Garzik {
1159*5b2fc499SJeff Garzik 	pegasus_t *pegasus = netdev_priv(dev);
1160*5b2fc499SJeff Garzik 	return pegasus->msg_enable;
1161*5b2fc499SJeff Garzik }
1162*5b2fc499SJeff Garzik 
1163*5b2fc499SJeff Garzik static void pegasus_set_msglevel(struct net_device *dev, u32 v)
1164*5b2fc499SJeff Garzik {
1165*5b2fc499SJeff Garzik 	pegasus_t *pegasus = netdev_priv(dev);
1166*5b2fc499SJeff Garzik 	pegasus->msg_enable = v;
1167*5b2fc499SJeff Garzik }
1168*5b2fc499SJeff Garzik 
1169*5b2fc499SJeff Garzik static struct ethtool_ops ops = {
1170*5b2fc499SJeff Garzik 	.get_drvinfo = pegasus_get_drvinfo,
1171*5b2fc499SJeff Garzik 	.get_settings = pegasus_get_settings,
1172*5b2fc499SJeff Garzik 	.set_settings = pegasus_set_settings,
1173*5b2fc499SJeff Garzik 	.nway_reset = pegasus_nway_reset,
1174*5b2fc499SJeff Garzik 	.get_link = pegasus_get_link,
1175*5b2fc499SJeff Garzik 	.get_msglevel = pegasus_get_msglevel,
1176*5b2fc499SJeff Garzik 	.set_msglevel = pegasus_set_msglevel,
1177*5b2fc499SJeff Garzik 	.get_wol = pegasus_get_wol,
1178*5b2fc499SJeff Garzik 	.set_wol = pegasus_set_wol,
1179*5b2fc499SJeff Garzik };
1180*5b2fc499SJeff Garzik 
1181*5b2fc499SJeff Garzik static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
1182*5b2fc499SJeff Garzik {
1183*5b2fc499SJeff Garzik 	__u16 *data = (__u16 *) & rq->ifr_ifru;
1184*5b2fc499SJeff Garzik 	pegasus_t *pegasus = netdev_priv(net);
1185*5b2fc499SJeff Garzik 	int res;
1186*5b2fc499SJeff Garzik 
1187*5b2fc499SJeff Garzik 	switch (cmd) {
1188*5b2fc499SJeff Garzik 	case SIOCDEVPRIVATE:
1189*5b2fc499SJeff Garzik 		data[0] = pegasus->phy;
1190*5b2fc499SJeff Garzik 	case SIOCDEVPRIVATE + 1:
1191*5b2fc499SJeff Garzik 		read_mii_word(pegasus, data[0], data[1] & 0x1f, &data[3]);
1192*5b2fc499SJeff Garzik 		res = 0;
1193*5b2fc499SJeff Garzik 		break;
1194*5b2fc499SJeff Garzik 	case SIOCDEVPRIVATE + 2:
1195*5b2fc499SJeff Garzik 		if (!capable(CAP_NET_ADMIN))
1196*5b2fc499SJeff Garzik 			return -EPERM;
1197*5b2fc499SJeff Garzik 		write_mii_word(pegasus, pegasus->phy, data[1] & 0x1f, data[2]);
1198*5b2fc499SJeff Garzik 		res = 0;
1199*5b2fc499SJeff Garzik 		break;
1200*5b2fc499SJeff Garzik 	default:
1201*5b2fc499SJeff Garzik 		res = -EOPNOTSUPP;
1202*5b2fc499SJeff Garzik 	}
1203*5b2fc499SJeff Garzik 	return res;
1204*5b2fc499SJeff Garzik }
1205*5b2fc499SJeff Garzik 
1206*5b2fc499SJeff Garzik static void pegasus_set_multicast(struct net_device *net)
1207*5b2fc499SJeff Garzik {
1208*5b2fc499SJeff Garzik 	pegasus_t *pegasus = netdev_priv(net);
1209*5b2fc499SJeff Garzik 
1210*5b2fc499SJeff Garzik 	if (net->flags & IFF_PROMISC) {
1211*5b2fc499SJeff Garzik 		pegasus->eth_regs[EthCtrl2] |= RX_PROMISCUOUS;
1212*5b2fc499SJeff Garzik 		if (netif_msg_link(pegasus))
1213*5b2fc499SJeff Garzik 			pr_info("%s: Promiscuous mode enabled.\n", net->name);
1214*5b2fc499SJeff Garzik 	} else if (net->mc_count ||
1215*5b2fc499SJeff Garzik 		   (net->flags & IFF_ALLMULTI)) {
1216*5b2fc499SJeff Garzik 		pegasus->eth_regs[EthCtrl0] |= RX_MULTICAST;
1217*5b2fc499SJeff Garzik 		pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS;
1218*5b2fc499SJeff Garzik 		if (netif_msg_link(pegasus))
1219*5b2fc499SJeff Garzik 			pr_info("%s: set allmulti\n", net->name);
1220*5b2fc499SJeff Garzik 	} else {
1221*5b2fc499SJeff Garzik 		pegasus->eth_regs[EthCtrl0] &= ~RX_MULTICAST;
1222*5b2fc499SJeff Garzik 		pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS;
1223*5b2fc499SJeff Garzik 	}
1224*5b2fc499SJeff Garzik 
1225*5b2fc499SJeff Garzik 	pegasus->flags |= ETH_REGS_CHANGE;
1226*5b2fc499SJeff Garzik 	ctrl_callback(pegasus->ctrl_urb);
1227*5b2fc499SJeff Garzik }
1228*5b2fc499SJeff Garzik 
1229*5b2fc499SJeff Garzik static __u8 mii_phy_probe(pegasus_t * pegasus)
1230*5b2fc499SJeff Garzik {
1231*5b2fc499SJeff Garzik 	int i;
1232*5b2fc499SJeff Garzik 	__u16 tmp;
1233*5b2fc499SJeff Garzik 
1234*5b2fc499SJeff Garzik 	for (i = 0; i < 32; i++) {
1235*5b2fc499SJeff Garzik 		read_mii_word(pegasus, i, MII_BMSR, &tmp);
1236*5b2fc499SJeff Garzik 		if (tmp == 0 || tmp == 0xffff || (tmp & BMSR_MEDIA) == 0)
1237*5b2fc499SJeff Garzik 			continue;
1238*5b2fc499SJeff Garzik 		else
1239*5b2fc499SJeff Garzik 			return i;
1240*5b2fc499SJeff Garzik 	}
1241*5b2fc499SJeff Garzik 
1242*5b2fc499SJeff Garzik 	return 0xff;
1243*5b2fc499SJeff Garzik }
1244*5b2fc499SJeff Garzik 
1245*5b2fc499SJeff Garzik static inline void setup_pegasus_II(pegasus_t * pegasus)
1246*5b2fc499SJeff Garzik {
1247*5b2fc499SJeff Garzik 	__u8 data = 0xa5;
1248*5b2fc499SJeff Garzik 
1249*5b2fc499SJeff Garzik 	set_register(pegasus, Reg1d, 0);
1250*5b2fc499SJeff Garzik 	set_register(pegasus, Reg7b, 1);
1251*5b2fc499SJeff Garzik 	mdelay(100);
1252*5b2fc499SJeff Garzik 	if ((pegasus->features & HAS_HOME_PNA) && mii_mode)
1253*5b2fc499SJeff Garzik 		set_register(pegasus, Reg7b, 0);
1254*5b2fc499SJeff Garzik 	else
1255*5b2fc499SJeff Garzik 		set_register(pegasus, Reg7b, 2);
1256*5b2fc499SJeff Garzik 
1257*5b2fc499SJeff Garzik 	set_register(pegasus, 0x83, data);
1258*5b2fc499SJeff Garzik 	get_registers(pegasus, 0x83, 1, &data);
1259*5b2fc499SJeff Garzik 
1260*5b2fc499SJeff Garzik 	if (data == 0xa5) {
1261*5b2fc499SJeff Garzik 		pegasus->chip = 0x8513;
1262*5b2fc499SJeff Garzik 	} else {
1263*5b2fc499SJeff Garzik 		pegasus->chip = 0;
1264*5b2fc499SJeff Garzik 	}
1265*5b2fc499SJeff Garzik 
1266*5b2fc499SJeff Garzik 	set_register(pegasus, 0x80, 0xc0);
1267*5b2fc499SJeff Garzik 	set_register(pegasus, 0x83, 0xff);
1268*5b2fc499SJeff Garzik 	set_register(pegasus, 0x84, 0x01);
1269*5b2fc499SJeff Garzik 
1270*5b2fc499SJeff Garzik 	if (pegasus->features & HAS_HOME_PNA && mii_mode)
1271*5b2fc499SJeff Garzik 		set_register(pegasus, Reg81, 6);
1272*5b2fc499SJeff Garzik 	else
1273*5b2fc499SJeff Garzik 		set_register(pegasus, Reg81, 2);
1274*5b2fc499SJeff Garzik }
1275*5b2fc499SJeff Garzik 
1276*5b2fc499SJeff Garzik 
1277*5b2fc499SJeff Garzik static struct workqueue_struct *pegasus_workqueue = NULL;
1278*5b2fc499SJeff Garzik #define CARRIER_CHECK_DELAY (2 * HZ)
1279*5b2fc499SJeff Garzik 
1280*5b2fc499SJeff Garzik static void check_carrier(struct work_struct *work)
1281*5b2fc499SJeff Garzik {
1282*5b2fc499SJeff Garzik 	pegasus_t *pegasus = container_of(work, pegasus_t, carrier_check.work);
1283*5b2fc499SJeff Garzik 	set_carrier(pegasus->net);
1284*5b2fc499SJeff Garzik 	if (!(pegasus->flags & PEGASUS_UNPLUG)) {
1285*5b2fc499SJeff Garzik 		queue_delayed_work(pegasus_workqueue, &pegasus->carrier_check,
1286*5b2fc499SJeff Garzik 			CARRIER_CHECK_DELAY);
1287*5b2fc499SJeff Garzik 	}
1288*5b2fc499SJeff Garzik }
1289*5b2fc499SJeff Garzik 
1290*5b2fc499SJeff Garzik static int pegasus_probe(struct usb_interface *intf,
1291*5b2fc499SJeff Garzik 			 const struct usb_device_id *id)
1292*5b2fc499SJeff Garzik {
1293*5b2fc499SJeff Garzik 	struct usb_device *dev = interface_to_usbdev(intf);
1294*5b2fc499SJeff Garzik 	struct net_device *net;
1295*5b2fc499SJeff Garzik 	pegasus_t *pegasus;
1296*5b2fc499SJeff Garzik 	int dev_index = id - pegasus_ids;
1297*5b2fc499SJeff Garzik 	int res = -ENOMEM;
1298*5b2fc499SJeff Garzik 
1299*5b2fc499SJeff Garzik 	usb_get_dev(dev);
1300*5b2fc499SJeff Garzik 	net = alloc_etherdev(sizeof(struct pegasus));
1301*5b2fc499SJeff Garzik 	if (!net) {
1302*5b2fc499SJeff Garzik 		dev_err(&intf->dev, "can't allocate %s\n", "device");
1303*5b2fc499SJeff Garzik 		goto out;
1304*5b2fc499SJeff Garzik 	}
1305*5b2fc499SJeff Garzik 
1306*5b2fc499SJeff Garzik 	pegasus = netdev_priv(net);
1307*5b2fc499SJeff Garzik 	memset(pegasus, 0, sizeof (struct pegasus));
1308*5b2fc499SJeff Garzik 	pegasus->dev_index = dev_index;
1309*5b2fc499SJeff Garzik 	init_waitqueue_head(&pegasus->ctrl_wait);
1310*5b2fc499SJeff Garzik 
1311*5b2fc499SJeff Garzik 	if (!alloc_urbs(pegasus)) {
1312*5b2fc499SJeff Garzik 		dev_err(&intf->dev, "can't allocate %s\n", "urbs");
1313*5b2fc499SJeff Garzik 		goto out1;
1314*5b2fc499SJeff Garzik 	}
1315*5b2fc499SJeff Garzik 
1316*5b2fc499SJeff Garzik 	tasklet_init(&pegasus->rx_tl, rx_fixup, (unsigned long) pegasus);
1317*5b2fc499SJeff Garzik 
1318*5b2fc499SJeff Garzik 	INIT_DELAYED_WORK(&pegasus->carrier_check, check_carrier);
1319*5b2fc499SJeff Garzik 
1320*5b2fc499SJeff Garzik 	pegasus->intf = intf;
1321*5b2fc499SJeff Garzik 	pegasus->usb = dev;
1322*5b2fc499SJeff Garzik 	pegasus->net = net;
1323*5b2fc499SJeff Garzik 	SET_MODULE_OWNER(net);
1324*5b2fc499SJeff Garzik 	net->open = pegasus_open;
1325*5b2fc499SJeff Garzik 	net->stop = pegasus_close;
1326*5b2fc499SJeff Garzik 	net->watchdog_timeo = PEGASUS_TX_TIMEOUT;
1327*5b2fc499SJeff Garzik 	net->tx_timeout = pegasus_tx_timeout;
1328*5b2fc499SJeff Garzik 	net->do_ioctl = pegasus_ioctl;
1329*5b2fc499SJeff Garzik 	net->hard_start_xmit = pegasus_start_xmit;
1330*5b2fc499SJeff Garzik 	net->set_multicast_list = pegasus_set_multicast;
1331*5b2fc499SJeff Garzik 	net->get_stats = pegasus_netdev_stats;
1332*5b2fc499SJeff Garzik 	SET_ETHTOOL_OPS(net, &ops);
1333*5b2fc499SJeff Garzik 	pegasus->mii.dev = net;
1334*5b2fc499SJeff Garzik 	pegasus->mii.mdio_read = mdio_read;
1335*5b2fc499SJeff Garzik 	pegasus->mii.mdio_write = mdio_write;
1336*5b2fc499SJeff Garzik 	pegasus->mii.phy_id_mask = 0x1f;
1337*5b2fc499SJeff Garzik 	pegasus->mii.reg_num_mask = 0x1f;
1338*5b2fc499SJeff Garzik 	spin_lock_init(&pegasus->rx_pool_lock);
1339*5b2fc499SJeff Garzik 	pegasus->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV
1340*5b2fc499SJeff Garzik 				| NETIF_MSG_PROBE | NETIF_MSG_LINK);
1341*5b2fc499SJeff Garzik 
1342*5b2fc499SJeff Garzik 	pegasus->features = usb_dev_id[dev_index].private;
1343*5b2fc499SJeff Garzik 	get_interrupt_interval(pegasus);
1344*5b2fc499SJeff Garzik 	if (reset_mac(pegasus)) {
1345*5b2fc499SJeff Garzik 		dev_err(&intf->dev, "can't reset MAC\n");
1346*5b2fc499SJeff Garzik 		res = -EIO;
1347*5b2fc499SJeff Garzik 		goto out2;
1348*5b2fc499SJeff Garzik 	}
1349*5b2fc499SJeff Garzik 	set_ethernet_addr(pegasus);
1350*5b2fc499SJeff Garzik 	fill_skb_pool(pegasus);
1351*5b2fc499SJeff Garzik 	if (pegasus->features & PEGASUS_II) {
1352*5b2fc499SJeff Garzik 		dev_info(&intf->dev, "setup Pegasus II specific registers\n");
1353*5b2fc499SJeff Garzik 		setup_pegasus_II(pegasus);
1354*5b2fc499SJeff Garzik 	}
1355*5b2fc499SJeff Garzik 	pegasus->phy = mii_phy_probe(pegasus);
1356*5b2fc499SJeff Garzik 	if (pegasus->phy == 0xff) {
1357*5b2fc499SJeff Garzik 		dev_warn(&intf->dev, "can't locate MII phy, using default\n");
1358*5b2fc499SJeff Garzik 		pegasus->phy = 1;
1359*5b2fc499SJeff Garzik 	}
1360*5b2fc499SJeff Garzik 	pegasus->mii.phy_id = pegasus->phy;
1361*5b2fc499SJeff Garzik 	usb_set_intfdata(intf, pegasus);
1362*5b2fc499SJeff Garzik 	SET_NETDEV_DEV(net, &intf->dev);
1363*5b2fc499SJeff Garzik 	pegasus_reset_wol(net);
1364*5b2fc499SJeff Garzik 	res = register_netdev(net);
1365*5b2fc499SJeff Garzik 	if (res)
1366*5b2fc499SJeff Garzik 		goto out3;
1367*5b2fc499SJeff Garzik 	queue_delayed_work(pegasus_workqueue, &pegasus->carrier_check,
1368*5b2fc499SJeff Garzik 				CARRIER_CHECK_DELAY);
1369*5b2fc499SJeff Garzik 
1370*5b2fc499SJeff Garzik 	dev_info(&intf->dev, "%s, %s, %02x:%02x:%02x:%02x:%02x:%02x\n",
1371*5b2fc499SJeff Garzik 		net->name,
1372*5b2fc499SJeff Garzik 		usb_dev_id[dev_index].name,
1373*5b2fc499SJeff Garzik 		net->dev_addr [0], net->dev_addr [1],
1374*5b2fc499SJeff Garzik 		net->dev_addr [2], net->dev_addr [3],
1375*5b2fc499SJeff Garzik 		net->dev_addr [4], net->dev_addr [5]);
1376*5b2fc499SJeff Garzik 	return 0;
1377*5b2fc499SJeff Garzik 
1378*5b2fc499SJeff Garzik out3:
1379*5b2fc499SJeff Garzik 	usb_set_intfdata(intf, NULL);
1380*5b2fc499SJeff Garzik 	free_skb_pool(pegasus);
1381*5b2fc499SJeff Garzik out2:
1382*5b2fc499SJeff Garzik 	free_all_urbs(pegasus);
1383*5b2fc499SJeff Garzik out1:
1384*5b2fc499SJeff Garzik 	free_netdev(net);
1385*5b2fc499SJeff Garzik out:
1386*5b2fc499SJeff Garzik 	usb_put_dev(dev);
1387*5b2fc499SJeff Garzik 	return res;
1388*5b2fc499SJeff Garzik }
1389*5b2fc499SJeff Garzik 
1390*5b2fc499SJeff Garzik static void pegasus_disconnect(struct usb_interface *intf)
1391*5b2fc499SJeff Garzik {
1392*5b2fc499SJeff Garzik 	struct pegasus *pegasus = usb_get_intfdata(intf);
1393*5b2fc499SJeff Garzik 
1394*5b2fc499SJeff Garzik 	usb_set_intfdata(intf, NULL);
1395*5b2fc499SJeff Garzik 	if (!pegasus) {
1396*5b2fc499SJeff Garzik 		dev_dbg(&intf->dev, "unregistering non-bound device?\n");
1397*5b2fc499SJeff Garzik 		return;
1398*5b2fc499SJeff Garzik 	}
1399*5b2fc499SJeff Garzik 
1400*5b2fc499SJeff Garzik 	pegasus->flags |= PEGASUS_UNPLUG;
1401*5b2fc499SJeff Garzik 	cancel_delayed_work(&pegasus->carrier_check);
1402*5b2fc499SJeff Garzik 	unregister_netdev(pegasus->net);
1403*5b2fc499SJeff Garzik 	usb_put_dev(interface_to_usbdev(intf));
1404*5b2fc499SJeff Garzik 	unlink_all_urbs(pegasus);
1405*5b2fc499SJeff Garzik 	free_all_urbs(pegasus);
1406*5b2fc499SJeff Garzik 	free_skb_pool(pegasus);
1407*5b2fc499SJeff Garzik 	if (pegasus->rx_skb != NULL) {
1408*5b2fc499SJeff Garzik 		dev_kfree_skb(pegasus->rx_skb);
1409*5b2fc499SJeff Garzik 		pegasus->rx_skb = NULL;
1410*5b2fc499SJeff Garzik 	}
1411*5b2fc499SJeff Garzik 	free_netdev(pegasus->net);
1412*5b2fc499SJeff Garzik }
1413*5b2fc499SJeff Garzik 
1414*5b2fc499SJeff Garzik static int pegasus_suspend (struct usb_interface *intf, pm_message_t message)
1415*5b2fc499SJeff Garzik {
1416*5b2fc499SJeff Garzik 	struct pegasus *pegasus = usb_get_intfdata(intf);
1417*5b2fc499SJeff Garzik 
1418*5b2fc499SJeff Garzik 	netif_device_detach (pegasus->net);
1419*5b2fc499SJeff Garzik 	cancel_delayed_work(&pegasus->carrier_check);
1420*5b2fc499SJeff Garzik 	if (netif_running(pegasus->net)) {
1421*5b2fc499SJeff Garzik 		usb_kill_urb(pegasus->rx_urb);
1422*5b2fc499SJeff Garzik 		usb_kill_urb(pegasus->intr_urb);
1423*5b2fc499SJeff Garzik 	}
1424*5b2fc499SJeff Garzik 	return 0;
1425*5b2fc499SJeff Garzik }
1426*5b2fc499SJeff Garzik 
1427*5b2fc499SJeff Garzik static int pegasus_resume (struct usb_interface *intf)
1428*5b2fc499SJeff Garzik {
1429*5b2fc499SJeff Garzik 	struct pegasus *pegasus = usb_get_intfdata(intf);
1430*5b2fc499SJeff Garzik 
1431*5b2fc499SJeff Garzik 	netif_device_attach (pegasus->net);
1432*5b2fc499SJeff Garzik 	if (netif_running(pegasus->net)) {
1433*5b2fc499SJeff Garzik 		pegasus->rx_urb->status = 0;
1434*5b2fc499SJeff Garzik 		pegasus->rx_urb->actual_length = 0;
1435*5b2fc499SJeff Garzik 		read_bulk_callback(pegasus->rx_urb);
1436*5b2fc499SJeff Garzik 
1437*5b2fc499SJeff Garzik 		pegasus->intr_urb->status = 0;
1438*5b2fc499SJeff Garzik 		pegasus->intr_urb->actual_length = 0;
1439*5b2fc499SJeff Garzik 		intr_callback(pegasus->intr_urb);
1440*5b2fc499SJeff Garzik 	}
1441*5b2fc499SJeff Garzik 	queue_delayed_work(pegasus_workqueue, &pegasus->carrier_check,
1442*5b2fc499SJeff Garzik 				CARRIER_CHECK_DELAY);
1443*5b2fc499SJeff Garzik 	return 0;
1444*5b2fc499SJeff Garzik }
1445*5b2fc499SJeff Garzik 
1446*5b2fc499SJeff Garzik static struct usb_driver pegasus_driver = {
1447*5b2fc499SJeff Garzik 	.name = driver_name,
1448*5b2fc499SJeff Garzik 	.probe = pegasus_probe,
1449*5b2fc499SJeff Garzik 	.disconnect = pegasus_disconnect,
1450*5b2fc499SJeff Garzik 	.id_table = pegasus_ids,
1451*5b2fc499SJeff Garzik 	.suspend = pegasus_suspend,
1452*5b2fc499SJeff Garzik 	.resume = pegasus_resume,
1453*5b2fc499SJeff Garzik };
1454*5b2fc499SJeff Garzik 
1455*5b2fc499SJeff Garzik static void parse_id(char *id)
1456*5b2fc499SJeff Garzik {
1457*5b2fc499SJeff Garzik 	unsigned int vendor_id=0, device_id=0, flags=0, i=0;
1458*5b2fc499SJeff Garzik 	char *token, *name=NULL;
1459*5b2fc499SJeff Garzik 
1460*5b2fc499SJeff Garzik 	if ((token = strsep(&id, ":")) != NULL)
1461*5b2fc499SJeff Garzik 		name = token;
1462*5b2fc499SJeff Garzik 	/* name now points to a null terminated string*/
1463*5b2fc499SJeff Garzik 	if ((token = strsep(&id, ":")) != NULL)
1464*5b2fc499SJeff Garzik 		vendor_id = simple_strtoul(token, NULL, 16);
1465*5b2fc499SJeff Garzik 	if ((token = strsep(&id, ":")) != NULL)
1466*5b2fc499SJeff Garzik 		device_id = simple_strtoul(token, NULL, 16);
1467*5b2fc499SJeff Garzik 	flags = simple_strtoul(id, NULL, 16);
1468*5b2fc499SJeff Garzik 	pr_info("%s: new device %s, vendor ID 0x%04x, device ID 0x%04x, flags: 0x%x\n",
1469*5b2fc499SJeff Garzik 	        driver_name, name, vendor_id, device_id, flags);
1470*5b2fc499SJeff Garzik 
1471*5b2fc499SJeff Garzik 	if (vendor_id > 0x10000 || vendor_id == 0)
1472*5b2fc499SJeff Garzik 		return;
1473*5b2fc499SJeff Garzik 	if (device_id > 0x10000 || device_id == 0)
1474*5b2fc499SJeff Garzik 		return;
1475*5b2fc499SJeff Garzik 
1476*5b2fc499SJeff Garzik 	for (i=0; usb_dev_id[i].name; i++);
1477*5b2fc499SJeff Garzik 	usb_dev_id[i].name = name;
1478*5b2fc499SJeff Garzik 	usb_dev_id[i].vendor = vendor_id;
1479*5b2fc499SJeff Garzik 	usb_dev_id[i].device = device_id;
1480*5b2fc499SJeff Garzik 	usb_dev_id[i].private = flags;
1481*5b2fc499SJeff Garzik 	pegasus_ids[i].match_flags = USB_DEVICE_ID_MATCH_DEVICE;
1482*5b2fc499SJeff Garzik 	pegasus_ids[i].idVendor = vendor_id;
1483*5b2fc499SJeff Garzik 	pegasus_ids[i].idProduct = device_id;
1484*5b2fc499SJeff Garzik }
1485*5b2fc499SJeff Garzik 
1486*5b2fc499SJeff Garzik static int __init pegasus_init(void)
1487*5b2fc499SJeff Garzik {
1488*5b2fc499SJeff Garzik 	pr_info("%s: %s, " DRIVER_DESC "\n", driver_name, DRIVER_VERSION);
1489*5b2fc499SJeff Garzik 	if (devid)
1490*5b2fc499SJeff Garzik 		parse_id(devid);
1491*5b2fc499SJeff Garzik 	pegasus_workqueue = create_singlethread_workqueue("pegasus");
1492*5b2fc499SJeff Garzik 	if (!pegasus_workqueue)
1493*5b2fc499SJeff Garzik 		return -ENOMEM;
1494*5b2fc499SJeff Garzik 	return usb_register(&pegasus_driver);
1495*5b2fc499SJeff Garzik }
1496*5b2fc499SJeff Garzik 
1497*5b2fc499SJeff Garzik static void __exit pegasus_exit(void)
1498*5b2fc499SJeff Garzik {
1499*5b2fc499SJeff Garzik 	destroy_workqueue(pegasus_workqueue);
1500*5b2fc499SJeff Garzik 	usb_deregister(&pegasus_driver);
1501*5b2fc499SJeff Garzik }
1502*5b2fc499SJeff Garzik 
1503*5b2fc499SJeff Garzik module_init(pegasus_init);
1504*5b2fc499SJeff Garzik module_exit(pegasus_exit);
1505