xref: /inferno-os/os/pc/ether2000.c (revision 6e425a9de8c003b5a733621a6b6730ec3cc902b8)
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "../port/error.h"
8 #include "../port/netif.h"
9 
10 #include "etherif.h"
11 #include "ether8390.h"
12 
13 /*
14  * Driver written for the 'Notebook Computer Ethernet LAN Adapter',
15  * a plug-in to the bus-slot on the rear of the Gateway NOMAD 425DXL
16  * laptop. The manual says NE2000 compatible.
17  * The interface appears to be pretty well described in the National
18  * Semiconductor Local Area Network Databook (1992) as one of the
19  * AT evaluation cards.
20  *
21  * The NE2000 is really just a DP8390[12] plus a data port
22  * and a reset port.
23  */
24 enum {
25 	Data		= 0x10,		/* offset from I/O base of data port */
26 	Reset		= 0x1F,		/* offset from I/O base of reset port */
27 };
28 
29 typedef struct Ctlr Ctlr;
30 typedef struct Ctlr {
31 	Pcidev*	pcidev;
32 	Ctlr*	next;
33 	int	active;
34 } Ctlr;
35 
36 static Ctlr* ctlrhead;
37 static Ctlr* ctlrtail;
38 
39 static struct {
40 	char*	name;
41 	int	id;
42 } ne2000pci[] = {
43 	{ "Realtek 8029",	(0x8029<<16)|0x10EC, },
44 	{ "Winbond 89C940",	(0x0940<<16)|0x1050, },
45 	{ nil },
46 };
47 
48 static Ctlr*
49 ne2000match(Ether* edev, int id)
50 {
51 	int port;
52 	Pcidev *p;
53 	Ctlr *ctlr;
54 
55 	/*
56 	 * Any adapter matches if no edev->port is supplied,
57 	 * otherwise the ports must match.
58 	 */
59 	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
60 		if(ctlr->active)
61 			continue;
62 		p = ctlr->pcidev;
63 		if(((p->did<<16)|p->vid) != id)
64 			continue;
65 		port = p->mem[0].bar & ~0x01;
66 		if(edev->port != 0 && edev->port != port)
67 			continue;
68 
69 		/*
70 		 * It suffices to fill these in,
71 		 * the rest is gleaned from the card.
72 		 */
73 		edev->port = port;
74 		edev->irq = p->intl;
75 
76 		ctlr->active = 1;
77 
78 		return ctlr;
79 	}
80 
81 	return nil;
82 }
83 
84 static void
85 ne2000pnp(Ether* edev)
86 {
87 	int i, id;
88 	Pcidev *p;
89 	Ctlr *ctlr;
90 
91 	/*
92 	 * Make a list of all ethernet controllers
93 	 * if not already done.
94 	 */
95 	if(ctlrhead == nil){
96 		p = nil;
97 		while(p = pcimatch(p, 0, 0)){
98 			if(p->ccrb != 0x02 || p->ccru != 0)
99 				continue;
100 			ctlr = malloc(sizeof(Ctlr));
101 			ctlr->pcidev = p;
102 
103 			if(ctlrhead != nil)
104 				ctlrtail->next = ctlr;
105 			else
106 				ctlrhead = ctlr;
107 			ctlrtail = ctlr;
108 		}
109 	}
110 
111 	/*
112 	 * Is it a card with an unrecognised vid+did?
113 	 * Normally a search is made through all the found controllers
114 	 * for one which matches any of the known vid+did pairs.
115 	 * If a vid+did pair is specified a search is made for that
116 	 * specific controller only.
117 	 */
118 	id = 0;
119 	for(i = 0; i < edev->nopt; i++){
120 		if(cistrncmp(edev->opt[i], "id=", 3) == 0)
121 			id = strtol(&edev->opt[i][3], nil, 0);
122 	}
123 
124 	if(id != 0)
125 		ne2000match(edev, id);
126 	else for(i = 0; ne2000pci[i].name; i++){
127 		if(ne2000match(edev, ne2000pci[i].id) != nil)
128 			break;
129 	}
130 }
131 
132 static int
133 ne2000reset(Ether* edev)
134 {
135 	static int first;
136 	ushort buf[16];
137 	ulong port;
138 	Dp8390 *dp8390;
139 	int i;
140 	uchar ea[Eaddrlen];
141 
142 	if(edev->port == 0)
143 		ne2000pnp(edev);
144 
145 	/*
146 	 * Set up the software configuration.
147 	 * Use defaults for irq, mem and size
148 	 * if not specified.
149 	 * Must have a port, no more default.
150 	 */
151 	if(edev->port == 0)
152 		return -1;
153 	if(edev->irq == 0)
154 		edev->irq = 2;
155 	if(edev->mem == 0)
156 		edev->mem = 0x4000;
157 	if(edev->size == 0)
158 		edev->size = 16*1024;
159 	port = edev->port;
160 
161 	if(ioalloc(edev->port, 0x20, 0, "ne2000") < 0)
162 		return -1;
163 
164 	edev->ctlr = malloc(sizeof(Dp8390));
165 	dp8390 = edev->ctlr;
166 	dp8390->width = 2;
167 	dp8390->ram = 0;
168 
169 	dp8390->port = port;
170 	dp8390->data = port+Data;
171 
172 	dp8390->tstart = HOWMANY(edev->mem, Dp8390BufSz);
173 	dp8390->pstart = dp8390->tstart + HOWMANY(sizeof(Etherpkt), Dp8390BufSz);
174 	dp8390->pstop = dp8390->tstart + HOWMANY(edev->size, Dp8390BufSz);
175 
176 	dp8390->dummyrr = 1;
177 	for(i = 0; i < edev->nopt; i++){
178 		if(strcmp(edev->opt[i], "nodummyrr"))
179 			continue;
180 		dp8390->dummyrr = 0;
181 		break;
182 	}
183 
184 	/*
185 	 * Reset the board. This is done by doing a read
186 	 * followed by a write to the Reset address.
187 	 */
188 	buf[0] = inb(port+Reset);
189 	delay(2);
190 	outb(port+Reset, buf[0]);
191 	delay(2);
192 
193 	/*
194 	 * Init the (possible) chip, then use the (possible)
195 	 * chip to read the (possible) PROM for ethernet address
196 	 * and a marker byte.
197 	 * Could just look at the DP8390 command register after
198 	 * initialisation has been tried, but that wouldn't be
199 	 * enough, there are other ethernet boards which could
200 	 * match.
201 	 */
202 	dp8390reset(edev);
203 	memset(buf, 0, sizeof(buf));
204 	dp8390read(dp8390, buf, 0, sizeof(buf));
205 	if((buf[0x0E] & 0xFF) != 0x57 || (buf[0x0F] & 0xFF) != 0x57){
206 		iofree(edev->port);
207 		free(edev->ctlr);
208 		return -1;
209 	}
210 
211 	/*
212 	 * Stupid machine. Shorts were asked for,
213 	 * shorts were delivered, although the PROM is a byte array.
214 	 * Set the ethernet address.
215 	 */
216 	memset(ea, 0, Eaddrlen);
217 	if(memcmp(ea, edev->ea, Eaddrlen) == 0){
218 		for(i = 0; i < sizeof(edev->ea); i++)
219 			edev->ea[i] = buf[i];
220 	}
221 	dp8390setea(edev);
222 
223 	return 0;
224 }
225 
226 void
227 ether2000link(void)
228 {
229 	addethercard("NE2000", ne2000reset);
230 }
231