xref: /inferno-os/os/pc/ether2000.c (revision 4eb166cf184c1f102fb79e31b1465ea3e2021c39)
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 	ushort buf[16];
136 	ulong port;
137 	Dp8390 *dp8390;
138 	int i;
139 	uchar ea[Eaddrlen];
140 
141 	if(edev->port == 0)
142 		ne2000pnp(edev);
143 
144 	/*
145 	 * Set up the software configuration.
146 	 * Use defaults for irq, mem and size
147 	 * if not specified.
148 	 * Must have a port, no more default.
149 	 */
150 	if(edev->port == 0)
151 		return -1;
152 	if(edev->irq == 0)
153 		edev->irq = 2;
154 	if(edev->mem == 0)
155 		edev->mem = 0x4000;
156 	if(edev->size == 0)
157 		edev->size = 16*1024;
158 	port = edev->port;
159 
160 	if(ioalloc(edev->port, 0x20, 0, "ne2000") < 0)
161 		return -1;
162 
163 	edev->ctlr = malloc(sizeof(Dp8390));
164 	dp8390 = edev->ctlr;
165 	dp8390->width = 2;
166 	dp8390->ram = 0;
167 
168 	dp8390->port = port;
169 	dp8390->data = port+Data;
170 
171 	dp8390->tstart = HOWMANY(edev->mem, Dp8390BufSz);
172 	dp8390->pstart = dp8390->tstart + HOWMANY(sizeof(Etherpkt), Dp8390BufSz);
173 	dp8390->pstop = dp8390->tstart + HOWMANY(edev->size, Dp8390BufSz);
174 
175 	dp8390->dummyrr = 1;
176 	for(i = 0; i < edev->nopt; i++){
177 		if(strcmp(edev->opt[i], "nodummyrr"))
178 			continue;
179 		dp8390->dummyrr = 0;
180 		break;
181 	}
182 
183 	/*
184 	 * Reset the board. This is done by doing a read
185 	 * followed by a write to the Reset address.
186 	 */
187 	buf[0] = inb(port+Reset);
188 	delay(2);
189 	outb(port+Reset, buf[0]);
190 	delay(2);
191 
192 	/*
193 	 * Init the (possible) chip, then use the (possible)
194 	 * chip to read the (possible) PROM for ethernet address
195 	 * and a marker byte.
196 	 * Could just look at the DP8390 command register after
197 	 * initialisation has been tried, but that wouldn't be
198 	 * enough, there are other ethernet boards which could
199 	 * match.
200 	 * Parallels has buf[0x0E] == 0x00 whereas real hardware
201 	 * usually has 0x57.
202 	 */
203 	dp8390reset(edev);
204 	memset(buf, 0, sizeof(buf));
205 	dp8390read(dp8390, buf, 0, sizeof(buf));
206 	i = buf[0x0E] & 0xFF;
207 	if((i != 0x00 && i != 0x57) || (buf[0x0F] & 0xFF) != 0x57){
208 		iofree(edev->port);
209 		free(edev->ctlr);
210 		return -1;
211 	}
212 
213 	/*
214 	 * Stupid machine. Shorts were asked for,
215 	 * shorts were delivered, although the PROM is a byte array.
216 	 * Set the ethernet address.
217 	 */
218 	memset(ea, 0, Eaddrlen);
219 	if(memcmp(ea, edev->ea, Eaddrlen) == 0){
220 		for(i = 0; i < sizeof(edev->ea); i++)
221 			edev->ea[i] = buf[i];
222 	}
223 	dp8390setea(edev);
224 
225 	return 0;
226 }
227 
228 void
229 ether2000link(void)
230 {
231 	addethercard("NE2000", ne2000reset);
232 }
233