xref: /plan9-contrib/sys/src/cmd/usb/ether/lan78xx.c (revision e1c0af539adcc53c770ef6e067291d1d2730e221)
1*e1c0af53SDavid du Colombier /*
2*e1c0af53SDavid du Colombier  * Microchip (ex SMSC) LAN78XX
3*e1c0af53SDavid du Colombier  *	 Also used as ethernet core in LAN7515 usb hub + ethernet
4*e1c0af53SDavid du Colombier  */
5*e1c0af53SDavid du Colombier 
6*e1c0af53SDavid du Colombier #include <u.h>
7*e1c0af53SDavid du Colombier #include <libc.h>
8*e1c0af53SDavid du Colombier #include <fcall.h>
9*e1c0af53SDavid du Colombier #include <thread.h>
10*e1c0af53SDavid du Colombier #include "usb.h"
11*e1c0af53SDavid du Colombier #include "usbfs.h"
12*e1c0af53SDavid du Colombier #include "ether.h"
13*e1c0af53SDavid du Colombier 
14*e1c0af53SDavid du Colombier enum {
15*e1c0af53SDavid du Colombier 	Doburst		= 1,
16*e1c0af53SDavid du Colombier 	Resettime	= 1000,
17*e1c0af53SDavid du Colombier 	E2pbusytime	= 1000,
18*e1c0af53SDavid du Colombier 	Hsburst		= 32,
19*e1c0af53SDavid du Colombier 	Defbulkdly	= 1000,
20*e1c0af53SDavid du Colombier 	Rxfifosize	= (12*1024),
21*e1c0af53SDavid du Colombier 	Txfifosize	= (12*1024),
22*e1c0af53SDavid du Colombier 
23*e1c0af53SDavid du Colombier 	MACoffset 	= 1,
24*e1c0af53SDavid du Colombier 	PHYinternal	= 1,
25*e1c0af53SDavid du Colombier 	Rxerror		= 0x00400000,
26*e1c0af53SDavid du Colombier 	Txfcs		= 1<<22,
27*e1c0af53SDavid du Colombier 
28*e1c0af53SDavid du Colombier 	/* USB vendor requests */
29*e1c0af53SDavid du Colombier 	Writereg	= 0xA0,
30*e1c0af53SDavid du Colombier 	Readreg		= 0xA1,
31*e1c0af53SDavid du Colombier 
32*e1c0af53SDavid du Colombier 	/* device registers */
33*e1c0af53SDavid du Colombier 	Idrev		= 0x00,
34*e1c0af53SDavid du Colombier 	Intsts		= 0x0C,
35*e1c0af53SDavid du Colombier 	Hwcfg		= 0x10,
36*e1c0af53SDavid du Colombier 		Led0en	= 1<<20,
37*e1c0af53SDavid du Colombier 		Led1en	= 1<<21,
38*e1c0af53SDavid du Colombier 		Mef	= 1<<4,
39*e1c0af53SDavid du Colombier 		Lrst	= 1<<1,
40*e1c0af53SDavid du Colombier 	Pmctrl		= 0x14,
41*e1c0af53SDavid du Colombier 		Ready	= 1<<7,
42*e1c0af53SDavid du Colombier 		Phyrst	= 1<<4,
43*e1c0af53SDavid du Colombier 	Gpiocfg0	= 0x18,
44*e1c0af53SDavid du Colombier 	Gpiocfg1	= 0x1C,
45*e1c0af53SDavid du Colombier 	E2pcmd		= 0x40,
46*e1c0af53SDavid du Colombier 		Busy	= 1<<31,
47*e1c0af53SDavid du Colombier 		Timeout	= 1<<10,
48*e1c0af53SDavid du Colombier 		Loaded	= 1<<9,
49*e1c0af53SDavid du Colombier 		Read	= 0,
50*e1c0af53SDavid du Colombier 	E2pdata		= 0x44,
51*e1c0af53SDavid du Colombier 	Burstcap	= 0x90,
52*e1c0af53SDavid du Colombier 	Intepctl	= 0x98,
53*e1c0af53SDavid du Colombier 		Phyint	= 1<<17,
54*e1c0af53SDavid du Colombier 	Bulkdelay	= 0x94,
55*e1c0af53SDavid du Colombier 	Rfectl		= 0xB0,
56*e1c0af53SDavid du Colombier 		Rxcoe	= 0xF<<11,
57*e1c0af53SDavid du Colombier 		Ab		= 1<<10,
58*e1c0af53SDavid du Colombier 		Am		= 1<<9,
59*e1c0af53SDavid du Colombier 		Au		= 1<<8,
60*e1c0af53SDavid du Colombier 		Dpf		= 1<<1,
61*e1c0af53SDavid du Colombier 	Usbcfg0		= 0x80,
62*e1c0af53SDavid du Colombier 		Bir	= 1<<6,
63*e1c0af53SDavid du Colombier 		Bce	= 1<<5,
64*e1c0af53SDavid du Colombier 	Usbcfg1		= 0x84,
65*e1c0af53SDavid du Colombier 	Rxfifoctl		= 0xC0,
66*e1c0af53SDavid du Colombier 		Rxen	= 1<<31,
67*e1c0af53SDavid du Colombier 	Txfifoctl		= 0xC4,
68*e1c0af53SDavid du Colombier 		Txen	= 1<<31,
69*e1c0af53SDavid du Colombier 	Rxfifo		= 0xC8,
70*e1c0af53SDavid du Colombier 	Txfifo		= 0xCc,
71*e1c0af53SDavid du Colombier 	Fctflow		= 0xD0,
72*e1c0af53SDavid du Colombier 	Maccr		= 0x100,
73*e1c0af53SDavid du Colombier 		Add		= 1<<12,
74*e1c0af53SDavid du Colombier 		Asd		= 1<<11,
75*e1c0af53SDavid du Colombier 	Macrx		= 0x104,
76*e1c0af53SDavid du Colombier 		Macfcs	= 1<<4,
77*e1c0af53SDavid du Colombier 		Macrxen	= 1<<0,
78*e1c0af53SDavid du Colombier 	Mactx		= 0x108,
79*e1c0af53SDavid du Colombier 		Mactxen	= 1<<0,
80*e1c0af53SDavid du Colombier 	Addrh		= 0x118,
81*e1c0af53SDavid du Colombier 	Addrl		= 0x11C,
82*e1c0af53SDavid du Colombier 	MIIaddr		= 0x120,
83*e1c0af53SDavid du Colombier 		MIIwrite= 1<<1,
84*e1c0af53SDavid du Colombier 		MIIread	= 0<<1,
85*e1c0af53SDavid du Colombier 		MIIbusy	= 1<<0,
86*e1c0af53SDavid du Colombier 	MIIdata		= 0x124,
87*e1c0af53SDavid du Colombier 	Flow		= 0x10C,
88*e1c0af53SDavid du Colombier 	Addrfilth	= 0x400,
89*e1c0af53SDavid du Colombier 		Afvalid	= 1<<31,
90*e1c0af53SDavid du Colombier 	Addrfiltl	= 0x404,
91*e1c0af53SDavid du Colombier 
92*e1c0af53SDavid du Colombier 	/* MII registers */
93*e1c0af53SDavid du Colombier 	Bmcr		= 0,
94*e1c0af53SDavid du Colombier 		Bmcrreset= 1<<15,
95*e1c0af53SDavid du Colombier 		Speed100= 1<<13,
96*e1c0af53SDavid du Colombier 		Anenable= 1<<12,
97*e1c0af53SDavid du Colombier 		Anrestart= 1<<9,
98*e1c0af53SDavid du Colombier 		Fulldpx	= 1<<8,
99*e1c0af53SDavid du Colombier 		Speed1000= 1<<6,
100*e1c0af53SDavid du Colombier 	Bmsr		= 1,
101*e1c0af53SDavid du Colombier 	Advertise	= 4,
102*e1c0af53SDavid du Colombier 		Adcsma	= 0x0001,
103*e1c0af53SDavid du Colombier 		Ad10h	= 0x0020,
104*e1c0af53SDavid du Colombier 		Ad10f	= 0x0040,
105*e1c0af53SDavid du Colombier 		Ad100h	= 0x0080,
106*e1c0af53SDavid du Colombier 		Ad100f	= 0x0100,
107*e1c0af53SDavid du Colombier 		Adpause	= 0x0400,
108*e1c0af53SDavid du Colombier 		Adpauseasym= 0x0800,
109*e1c0af53SDavid du Colombier 		Adall	= Ad10h|Ad10f|Ad100h|Ad100f,
110*e1c0af53SDavid du Colombier 	Lpa		= 5,
111*e1c0af53SDavid du Colombier 	Ctrl1000	= 9,
112*e1c0af53SDavid du Colombier 		Ad1000h = 0x0400,
113*e1c0af53SDavid du Colombier 		Ad1000f = 0x0200,
114*e1c0af53SDavid du Colombier 	Ledmodes	= 29,
115*e1c0af53SDavid du Colombier 		Led0shift = 0,
116*e1c0af53SDavid du Colombier 		Led1shift = 4,
117*e1c0af53SDavid du Colombier 		Linkact = 0x0,
118*e1c0af53SDavid du Colombier 		Link1000 = 0x1,
119*e1c0af53SDavid du Colombier 	Phyintmask	= 25,
120*e1c0af53SDavid du Colombier 		Anegcomp= 1<<10,
121*e1c0af53SDavid du Colombier 		Linkchg = 1<<13,
122*e1c0af53SDavid du Colombier };
123*e1c0af53SDavid du Colombier 
124*e1c0af53SDavid du Colombier static int burstcap = Hsburst, bulkdelay = Defbulkdly;
125*e1c0af53SDavid du Colombier 
126*e1c0af53SDavid du Colombier static int
wr(Dev * d,int reg,int val)127*e1c0af53SDavid du Colombier wr(Dev *d, int reg, int val)
128*e1c0af53SDavid du Colombier {
129*e1c0af53SDavid du Colombier 	int ret;
130*e1c0af53SDavid du Colombier 
131*e1c0af53SDavid du Colombier 	ret = usbcmd(d, Rh2d|Rvendor|Rdev, Writereg, 0, reg,
132*e1c0af53SDavid du Colombier 		(uchar*)&val, sizeof(val));
133*e1c0af53SDavid du Colombier 	if(ret < 0)
134*e1c0af53SDavid du Colombier 		deprint(2, "%s: wr(%x, %x): %r", argv0, reg, val);
135*e1c0af53SDavid du Colombier 	return ret;
136*e1c0af53SDavid du Colombier }
137*e1c0af53SDavid du Colombier 
138*e1c0af53SDavid du Colombier static int
rr(Dev * d,int reg)139*e1c0af53SDavid du Colombier rr(Dev *d, int reg)
140*e1c0af53SDavid du Colombier {
141*e1c0af53SDavid du Colombier 	int ret, rval;
142*e1c0af53SDavid du Colombier 
143*e1c0af53SDavid du Colombier 	ret = usbcmd(d, Rd2h|Rvendor|Rdev, Readreg, 0, reg,
144*e1c0af53SDavid du Colombier 		(uchar*)&rval, sizeof(rval));
145*e1c0af53SDavid du Colombier 	if(ret < 0){
146*e1c0af53SDavid du Colombier 		fprint(2, "%s: rr(%x): %r", argv0, reg);
147*e1c0af53SDavid du Colombier 		return 0;
148*e1c0af53SDavid du Colombier 	}
149*e1c0af53SDavid du Colombier 	return rval;
150*e1c0af53SDavid du Colombier }
151*e1c0af53SDavid du Colombier 
152*e1c0af53SDavid du Colombier static int
miird(Dev * d,int idx)153*e1c0af53SDavid du Colombier miird(Dev *d, int idx)
154*e1c0af53SDavid du Colombier {
155*e1c0af53SDavid du Colombier 	while(rr(d, MIIaddr) & MIIbusy)
156*e1c0af53SDavid du Colombier 		;
157*e1c0af53SDavid du Colombier 	wr(d, MIIaddr, PHYinternal<<11 | idx<<6 | MIIread | MIIbusy);
158*e1c0af53SDavid du Colombier 	while(rr(d, MIIaddr) & MIIbusy)
159*e1c0af53SDavid du Colombier 		;
160*e1c0af53SDavid du Colombier 	return rr(d, MIIdata);
161*e1c0af53SDavid du Colombier }
162*e1c0af53SDavid du Colombier 
163*e1c0af53SDavid du Colombier static void
miiwr(Dev * d,int idx,int val)164*e1c0af53SDavid du Colombier miiwr(Dev *d, int idx, int val)
165*e1c0af53SDavid du Colombier {
166*e1c0af53SDavid du Colombier 	while(rr(d, MIIaddr) & MIIbusy)
167*e1c0af53SDavid du Colombier 		;
168*e1c0af53SDavid du Colombier 	wr(d, MIIdata, val);
169*e1c0af53SDavid du Colombier 	wr(d, MIIaddr, PHYinternal<<11 | idx<<6 | MIIwrite | MIIbusy);
170*e1c0af53SDavid du Colombier 	while(rr(d, MIIaddr) & MIIbusy)
171*e1c0af53SDavid du Colombier 		;
172*e1c0af53SDavid du Colombier }
173*e1c0af53SDavid du Colombier 
174*e1c0af53SDavid du Colombier static int
eepromr(Dev * d,int off,uchar * buf,int len)175*e1c0af53SDavid du Colombier eepromr(Dev *d, int off, uchar *buf, int len)
176*e1c0af53SDavid du Colombier {
177*e1c0af53SDavid du Colombier 	int i, v;
178*e1c0af53SDavid du Colombier 
179*e1c0af53SDavid du Colombier 	for(i = 0; i < E2pbusytime; i++)
180*e1c0af53SDavid du Colombier 		if((rr(d, E2pcmd) & Busy) == 0)
181*e1c0af53SDavid du Colombier 			break;
182*e1c0af53SDavid du Colombier 	if(i == E2pbusytime)
183*e1c0af53SDavid du Colombier 		return -1;
184*e1c0af53SDavid du Colombier 	for(i = 0; i < len; i++){
185*e1c0af53SDavid du Colombier 		wr(d, E2pcmd, Busy|Read|(i+off));
186*e1c0af53SDavid du Colombier 		while((v = rr(d, E2pcmd) & (Busy|Timeout)) == Busy)
187*e1c0af53SDavid du Colombier 			;
188*e1c0af53SDavid du Colombier 		if(v & Timeout)
189*e1c0af53SDavid du Colombier 			return -1;
190*e1c0af53SDavid du Colombier 		buf[i] = rr(d, E2pdata);
191*e1c0af53SDavid du Colombier 	}
192*e1c0af53SDavid du Colombier 	return 0;
193*e1c0af53SDavid du Colombier }
194*e1c0af53SDavid du Colombier 
195*e1c0af53SDavid du Colombier static void
phyinit(Dev * d)196*e1c0af53SDavid du Colombier phyinit(Dev *d)
197*e1c0af53SDavid du Colombier {
198*e1c0af53SDavid du Colombier 	int i;
199*e1c0af53SDavid du Colombier 
200*e1c0af53SDavid du Colombier 	miiwr(d, Bmcr, Bmcrreset|Anenable);
201*e1c0af53SDavid du Colombier 	for(i = 0; i < Resettime/10; i++){
202*e1c0af53SDavid du Colombier 		if((miird(d, Bmcr) & Bmcrreset) == 0)
203*e1c0af53SDavid du Colombier 			break;
204*e1c0af53SDavid du Colombier 		sleep(10);
205*e1c0af53SDavid du Colombier 	}
206*e1c0af53SDavid du Colombier 	miiwr(d, Advertise, Adcsma|Adall|Adpause|Adpauseasym);
207*e1c0af53SDavid du Colombier 	miiwr(d, Ctrl1000, Ad1000f);
208*e1c0af53SDavid du Colombier 	miiwr(d, Phyintmask, 0);
209*e1c0af53SDavid du Colombier 	miiwr(d, Ledmodes, (Linkact<<Led1shift) | (Link1000<<Led0shift));
210*e1c0af53SDavid du Colombier 	miiwr(d, Bmcr, miird(d, Bmcr)|Anenable|Anrestart);
211*e1c0af53SDavid du Colombier }
212*e1c0af53SDavid du Colombier 
213*e1c0af53SDavid du Colombier 
214*e1c0af53SDavid du Colombier static int
doreset(Dev * d,int reg,int bit)215*e1c0af53SDavid du Colombier doreset(Dev *d, int reg, int bit)
216*e1c0af53SDavid du Colombier {
217*e1c0af53SDavid du Colombier 	int i;
218*e1c0af53SDavid du Colombier 
219*e1c0af53SDavid du Colombier 	if(wr(d, reg, bit) < 0)
220*e1c0af53SDavid du Colombier 		return -1;
221*e1c0af53SDavid du Colombier 	for(i = 0; i < Resettime/10; i++){
222*e1c0af53SDavid du Colombier 		 if((rr(d, reg) & bit) == 0)
223*e1c0af53SDavid du Colombier 			return 1;
224*e1c0af53SDavid du Colombier 		sleep(10);
225*e1c0af53SDavid du Colombier 	}
226*e1c0af53SDavid du Colombier 	return 0;
227*e1c0af53SDavid du Colombier }
228*e1c0af53SDavid du Colombier 
229*e1c0af53SDavid du Colombier static int
getmac(Dev * d,uchar buf[])230*e1c0af53SDavid du Colombier getmac(Dev *d, uchar buf[])
231*e1c0af53SDavid du Colombier {
232*e1c0af53SDavid du Colombier 	int i;
233*e1c0af53SDavid du Colombier 	uchar ea[Eaddrlen];
234*e1c0af53SDavid du Colombier 
235*e1c0af53SDavid du Colombier 	if(eepromr(d, MACoffset, ea, Eaddrlen) < 0)
236*e1c0af53SDavid du Colombier 		return -1;
237*e1c0af53SDavid du Colombier 	for(i = 0; i < Eaddrlen; i++)
238*e1c0af53SDavid du Colombier 		if(ea[i] != 0 && ea[i] != 0xFF){
239*e1c0af53SDavid du Colombier 			memmove(buf, ea, Eaddrlen);
240*e1c0af53SDavid du Colombier 			break;
241*e1c0af53SDavid du Colombier 		}
242*e1c0af53SDavid du Colombier 	return Eaddrlen;
243*e1c0af53SDavid du Colombier }
244*e1c0af53SDavid du Colombier 
245*e1c0af53SDavid du Colombier static int
lan78xxinit(Ether * ether)246*e1c0af53SDavid du Colombier lan78xxinit(Ether *ether)
247*e1c0af53SDavid du Colombier {
248*e1c0af53SDavid du Colombier 	Dev *d;
249*e1c0af53SDavid du Colombier 	u32int a;
250*e1c0af53SDavid du Colombier 	int i;
251*e1c0af53SDavid du Colombier 
252*e1c0af53SDavid du Colombier 	if(ether->cid != S78xx)
253*e1c0af53SDavid du Colombier 		return -1;
254*e1c0af53SDavid du Colombier 	d = ether->dev;
255*e1c0af53SDavid du Colombier 	deprint(2, "%s: setting up LAN78XX\n", argv0);
256*e1c0af53SDavid du Colombier 	deprint(2, "chip id/rev = %8.8ux\n", rr(d, Idrev));
257*e1c0af53SDavid du Colombier 	if(!doreset(d, Hwcfg, Lrst) || !doreset(d, Pmctrl, Phyrst))
258*e1c0af53SDavid du Colombier 		return -1;
259*e1c0af53SDavid du Colombier 	for(i = 0; i < Resettime/10; i++){
260*e1c0af53SDavid du Colombier 		 if(rr(d, Pmctrl) & Ready)
261*e1c0af53SDavid du Colombier 			break;
262*e1c0af53SDavid du Colombier 		sleep(10);
263*e1c0af53SDavid du Colombier 	}
264*e1c0af53SDavid du Colombier 	if((rr(d, Pmctrl) & Ready) == 0){
265*e1c0af53SDavid du Colombier 		deprint(2, "%s: device not ready after reset\n", argv0);
266*e1c0af53SDavid du Colombier 		return -1;
267*e1c0af53SDavid du Colombier 	}
268*e1c0af53SDavid du Colombier 	if(getmac(d, ether->addr) < 0)
269*e1c0af53SDavid du Colombier 		deprint(2, "%s: can't read etheraddr from EEPROM\n", argv0);
270*e1c0af53SDavid du Colombier 	a = GET4(ether->addr);
271*e1c0af53SDavid du Colombier 	wr(d, Addrl, a);
272*e1c0af53SDavid du Colombier 	wr(d, Addrfiltl, a);
273*e1c0af53SDavid du Colombier 	a = GET2(ether->addr+4);
274*e1c0af53SDavid du Colombier 	wr(d, Addrh, a);
275*e1c0af53SDavid du Colombier 	wr(d, Addrfilth, a|Afvalid);
276*e1c0af53SDavid du Colombier 	deprint(2, "Address filter %8.8ux %8.8ux\n", rr(d, Addrfilth), rr(d, Addrfiltl));
277*e1c0af53SDavid du Colombier 
278*e1c0af53SDavid du Colombier 	wr(d, Usbcfg0, rr(d, Usbcfg0) | Bir);
279*e1c0af53SDavid du Colombier 	if(Doburst){
280*e1c0af53SDavid du Colombier 		wr(d, Hwcfg, rr(d, Hwcfg)|Mef);
281*e1c0af53SDavid du Colombier 		wr(d, Usbcfg0, rr(d, Usbcfg0)|Bce);
282*e1c0af53SDavid du Colombier 		wr(d, Burstcap, burstcap);
283*e1c0af53SDavid du Colombier 		wr(d, Bulkdelay, bulkdelay);
284*e1c0af53SDavid du Colombier 	}else{
285*e1c0af53SDavid du Colombier 		wr(d, Hwcfg, rr(d, Hwcfg)&~Mef);
286*e1c0af53SDavid du Colombier 		wr(d, Usbcfg0, rr(d, Usbcfg0)&~Bce);
287*e1c0af53SDavid du Colombier 		wr(d, Burstcap, 0);
288*e1c0af53SDavid du Colombier 		wr(d, Bulkdelay, 0);
289*e1c0af53SDavid du Colombier 	}
290*e1c0af53SDavid du Colombier 	wr(d, Rxfifo, (Rxfifosize-512)/512);
291*e1c0af53SDavid du Colombier 	wr(d, Txfifo, (Txfifosize-512)/512);
292*e1c0af53SDavid du Colombier 	wr(d, Intsts, ~0);
293*e1c0af53SDavid du Colombier 	wr(d, Hwcfg, rr(d, Hwcfg) | Led0en|Led1en);
294*e1c0af53SDavid du Colombier 	wr(d, Flow, 0);
295*e1c0af53SDavid du Colombier 	wr(d, Fctflow, 0);
296*e1c0af53SDavid du Colombier 	wr(d, Rfectl, (rr(d, Rfectl) & ~Rxcoe) | Ab|Dpf); /* TODO could offload checksums? */
297*e1c0af53SDavid du Colombier 
298*e1c0af53SDavid du Colombier 	phyinit(d);
299*e1c0af53SDavid du Colombier 
300*e1c0af53SDavid du Colombier 	wr(d, Maccr, rr(d,Maccr)|Add|Asd);
301*e1c0af53SDavid du Colombier 
302*e1c0af53SDavid du Colombier 	wr(d, Intepctl, rr(d, Intepctl)|Phyint);
303*e1c0af53SDavid du Colombier 	wr(d, Mactx, Mactxen);
304*e1c0af53SDavid du Colombier 	wr(d, Macrx, rr(d, Macrx) | Macfcs|Macrxen);
305*e1c0af53SDavid du Colombier 	wr(d, Txfifoctl, Txen);
306*e1c0af53SDavid du Colombier 	wr(d, Rxfifoctl, Rxen);
307*e1c0af53SDavid du Colombier 
308*e1c0af53SDavid du Colombier 	return 0;
309*e1c0af53SDavid du Colombier }
310*e1c0af53SDavid du Colombier 
311*e1c0af53SDavid du Colombier static long
lan78xxbread(Ether * e,Buf * bp)312*e1c0af53SDavid du Colombier lan78xxbread(Ether *e, Buf *bp)
313*e1c0af53SDavid du Colombier {
314*e1c0af53SDavid du Colombier 	uint hd;
315*e1c0af53SDavid du Colombier 	int n, m;
316*e1c0af53SDavid du Colombier 	Buf *rbp;
317*e1c0af53SDavid du Colombier 
318*e1c0af53SDavid du Colombier 	rbp = e->aux;
319*e1c0af53SDavid du Colombier 	if(rbp->ndata < 10){
320*e1c0af53SDavid du Colombier 		rbp->rp = rbp->data;
321*e1c0af53SDavid du Colombier 		rbp->ndata = read(e->epin->dfd, rbp->rp, Doburst? burstcap*512:
322*e1c0af53SDavid du Colombier 			Maxpkt);
323*e1c0af53SDavid du Colombier 		if(rbp->ndata < 0)
324*e1c0af53SDavid du Colombier 			return -1;
325*e1c0af53SDavid du Colombier 	}
326*e1c0af53SDavid du Colombier 	if(rbp->ndata < 10){
327*e1c0af53SDavid du Colombier 		werrstr("short frame");
328*e1c0af53SDavid du Colombier 		fprint(2, "lan78xx short frame %d bytes\n", rbp->ndata);
329*e1c0af53SDavid du Colombier 		sleep(1000);
330*e1c0af53SDavid du Colombier 		return 0;
331*e1c0af53SDavid du Colombier 	}
332*e1c0af53SDavid du Colombier 	hd = GET4(rbp->rp);
333*e1c0af53SDavid du Colombier 	n = hd & 0x3FFF;
334*e1c0af53SDavid du Colombier 	rbp->rp += 10;
335*e1c0af53SDavid du Colombier 	rbp->ndata -= 10;
336*e1c0af53SDavid du Colombier 	if(n < 6 || n > rbp->ndata){
337*e1c0af53SDavid du Colombier 		werrstr("frame length");
338*e1c0af53SDavid du Colombier 		fprint(2, "lan78xx length error packet %d buf %d\n", n, rbp->ndata);
339*e1c0af53SDavid du Colombier 		rbp->ndata = 0;
340*e1c0af53SDavid du Colombier 		return 0;
341*e1c0af53SDavid du Colombier 	}
342*e1c0af53SDavid du Colombier 	if(hd & Rxerror){
343*e1c0af53SDavid du Colombier 		fprint(2, "lan78xx rx error %8.8ux\n", hd);
344*e1c0af53SDavid du Colombier 		n = 0;
345*e1c0af53SDavid du Colombier 	}else{
346*e1c0af53SDavid du Colombier 		bp->rp = bp->data + Hdrsize;
347*e1c0af53SDavid du Colombier 		memmove(bp->rp, rbp->rp, n);
348*e1c0af53SDavid du Colombier 	}
349*e1c0af53SDavid du Colombier 	bp->ndata = n;
350*e1c0af53SDavid du Colombier 	rbp->rp += n;
351*e1c0af53SDavid du Colombier 	rbp->ndata -= n;
352*e1c0af53SDavid du Colombier 	if(rbp->ndata > 0){
353*e1c0af53SDavid du Colombier 		m = rbp->rp - rbp->data;
354*e1c0af53SDavid du Colombier 		if(m&3){
355*e1c0af53SDavid du Colombier 			m = 4 - (m&3);
356*e1c0af53SDavid du Colombier 			rbp->rp += m;
357*e1c0af53SDavid du Colombier 			rbp->ndata -= m;
358*e1c0af53SDavid du Colombier 		}
359*e1c0af53SDavid du Colombier 	}
360*e1c0af53SDavid du Colombier 	return n;
361*e1c0af53SDavid du Colombier }
362*e1c0af53SDavid du Colombier 
363*e1c0af53SDavid du Colombier static long
lan78xxbwrite(Ether * e,Buf * bp)364*e1c0af53SDavid du Colombier lan78xxbwrite(Ether *e, Buf *bp)
365*e1c0af53SDavid du Colombier {
366*e1c0af53SDavid du Colombier 	int n;
367*e1c0af53SDavid du Colombier 
368*e1c0af53SDavid du Colombier 	n = bp->ndata & 0xFFFFF;
369*e1c0af53SDavid du Colombier 	bp->rp -= 8;
370*e1c0af53SDavid du Colombier 	bp->ndata += 8;
371*e1c0af53SDavid du Colombier 	PUT4(bp->rp, n | Txfcs);
372*e1c0af53SDavid du Colombier 	PUT4(bp->rp+4, 0);
373*e1c0af53SDavid du Colombier 	n = write(e->epout->dfd, bp->rp, bp->ndata);
374*e1c0af53SDavid du Colombier 	if(n != bp->ndata)
375*e1c0af53SDavid du Colombier 		deprint(2, "bwrite %d: %r\n", n);
376*e1c0af53SDavid du Colombier 	return n;
377*e1c0af53SDavid du Colombier }
378*e1c0af53SDavid du Colombier 
379*e1c0af53SDavid du Colombier static int
lan78xxpromiscuous(Ether * e,int on)380*e1c0af53SDavid du Colombier lan78xxpromiscuous(Ether *e, int on)
381*e1c0af53SDavid du Colombier {
382*e1c0af53SDavid du Colombier 	Dev *d;
383*e1c0af53SDavid du Colombier 	int rxctl;
384*e1c0af53SDavid du Colombier 
385*e1c0af53SDavid du Colombier 	d = e->dev;
386*e1c0af53SDavid du Colombier 	rxctl = rr(d, Rfectl);
387*e1c0af53SDavid du Colombier 	if(on)
388*e1c0af53SDavid du Colombier 		rxctl |= Am|Au;
389*e1c0af53SDavid du Colombier 	else
390*e1c0af53SDavid du Colombier 		rxctl &= ~(Am|Au);
391*e1c0af53SDavid du Colombier 	return wr(d, Rfectl, rxctl);
392*e1c0af53SDavid du Colombier }
393*e1c0af53SDavid du Colombier 
394*e1c0af53SDavid du Colombier static int
lan78xxmulticast(Ether * e,uchar * addr,int on)395*e1c0af53SDavid du Colombier lan78xxmulticast(Ether *e, uchar *addr, int on)
396*e1c0af53SDavid du Colombier {
397*e1c0af53SDavid du Colombier 	int rxctl;
398*e1c0af53SDavid du Colombier 	Dev *d;
399*e1c0af53SDavid du Colombier 
400*e1c0af53SDavid du Colombier 	USED(addr, on);
401*e1c0af53SDavid du Colombier 	/* BUG: should write multicast filter */
402*e1c0af53SDavid du Colombier 	d = e->dev;
403*e1c0af53SDavid du Colombier 	rxctl = rr(d, Rfectl);
404*e1c0af53SDavid du Colombier 	if(e->nmcasts != 0)
405*e1c0af53SDavid du Colombier 		rxctl |= Am;
406*e1c0af53SDavid du Colombier 	else
407*e1c0af53SDavid du Colombier 		rxctl &= ~Am;
408*e1c0af53SDavid du Colombier 	deprint(2, "%s: lan78xxmulticast %d\n", argv0, e->nmcasts);
409*e1c0af53SDavid du Colombier 	return wr(d, Rfectl, rxctl);
410*e1c0af53SDavid du Colombier }
411*e1c0af53SDavid du Colombier 
412*e1c0af53SDavid du Colombier static void
lan78xxfree(Ether * ether)413*e1c0af53SDavid du Colombier lan78xxfree(Ether *ether)
414*e1c0af53SDavid du Colombier {
415*e1c0af53SDavid du Colombier 	free(ether->aux);
416*e1c0af53SDavid du Colombier 	ether->aux = nil;
417*e1c0af53SDavid du Colombier }
418*e1c0af53SDavid du Colombier 
419*e1c0af53SDavid du Colombier int
lan78xxreset(Ether * ether)420*e1c0af53SDavid du Colombier lan78xxreset(Ether *ether)
421*e1c0af53SDavid du Colombier {
422*e1c0af53SDavid du Colombier 	Cinfo *ip;
423*e1c0af53SDavid du Colombier 	Dev *dev;
424*e1c0af53SDavid du Colombier 
425*e1c0af53SDavid du Colombier 	dev = ether->dev;
426*e1c0af53SDavid du Colombier 	for(ip = cinfo; ip->vid != 0; ip++)
427*e1c0af53SDavid du Colombier 		if(ip->vid == dev->usb->vid && ip->did == dev->usb->did){
428*e1c0af53SDavid du Colombier 			ether->cid = ip->cid;
429*e1c0af53SDavid du Colombier 			if(lan78xxinit(ether) < 0){
430*e1c0af53SDavid du Colombier 				deprint(2, "%s: lan78xx init failed: %r\n", argv0);
431*e1c0af53SDavid du Colombier 				return -1;
432*e1c0af53SDavid du Colombier 			}
433*e1c0af53SDavid du Colombier 			deprint(2, "%s: lan78xx reset done\n", argv0);
434*e1c0af53SDavid du Colombier 			ether->name = "lan78xx";
435*e1c0af53SDavid du Colombier 			if(Doburst){
436*e1c0af53SDavid du Colombier 				ether->bufsize = burstcap*512;
437*e1c0af53SDavid du Colombier 				ether->aux = emallocz(sizeof(Buf) +
438*e1c0af53SDavid du Colombier 					ether->bufsize - Maxpkt, 1);
439*e1c0af53SDavid du Colombier 			}else{
440*e1c0af53SDavid du Colombier 				ether->bufsize = Maxpkt;
441*e1c0af53SDavid du Colombier 				ether->aux = emallocz(sizeof(Buf), 1);
442*e1c0af53SDavid du Colombier 			}
443*e1c0af53SDavid du Colombier 			ether->free = lan78xxfree;
444*e1c0af53SDavid du Colombier 			ether->bread = lan78xxbread;
445*e1c0af53SDavid du Colombier 			ether->bwrite = lan78xxbwrite;
446*e1c0af53SDavid du Colombier 			ether->promiscuous = lan78xxpromiscuous;
447*e1c0af53SDavid du Colombier 			ether->multicast = lan78xxmulticast;
448*e1c0af53SDavid du Colombier 			ether->mbps = 100;	/* BUG */
449*e1c0af53SDavid du Colombier 			return 0;
450*e1c0af53SDavid du Colombier 		}
451*e1c0af53SDavid du Colombier 	return -1;
452*e1c0af53SDavid du Colombier }
453