]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/ether2000.c
fix kernel: pio()/mfreeseg() race
[plan9front.git] / sys / src / 9 / pc / ether2000.c
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 }