Commit | Line | Data |
---|---|---|
da7c5cc6 KM |
1 | /* |
2 | * Copyright (c) 1982 Regents of the University of California. | |
3 | * All rights reserved. The Berkeley software License Agreement | |
4 | * specifies the terms and conditions for redistribution. | |
5 | * | |
87c6a528 | 6 | * @(#)if_ex.c 6.11 (Berkeley) %G% |
da7c5cc6 KM |
7 | */ |
8 | ||
063e82dc MK |
9 | |
10 | #include "ex.h" | |
9a0b0c74 | 11 | #if NEX > 0 |
063e82dc MK |
12 | |
13 | /* | |
14 | * Excelan EXOS 204 Interface | |
15 | * | |
16 | * George Powers | |
17 | * Excelan Inc. | |
18 | */ | |
19 | ||
20 | #include "../machine/pte.h" | |
21 | ||
1a568327 MK |
22 | #include "param.h" |
23 | #include "systm.h" | |
24 | #include "mbuf.h" | |
25 | #include "buf.h" | |
26 | #include "protosw.h" | |
27 | #include "socket.h" | |
28 | #include "vmmac.h" | |
29 | #include "ioctl.h" | |
30 | #include "errno.h" | |
063e82dc MK |
31 | |
32 | #include "../net/if.h" | |
33 | #include "../net/netisr.h" | |
34 | #include "../net/route.h" | |
7bdbbe60 | 35 | |
7bdbbe60 | 36 | #ifdef INET |
063e82dc MK |
37 | #include "../netinet/in.h" |
38 | #include "../netinet/in_systm.h" | |
1a568327 | 39 | #include "../netinet/in_var.h" |
063e82dc | 40 | #include "../netinet/ip.h" |
063e82dc | 41 | #include "../netinet/if_ether.h" |
7bdbbe60 MK |
42 | #endif |
43 | ||
f2803948 KS |
44 | #ifdef NS |
45 | #include "../netns/ns.h" | |
46 | #include "../netns/ns_if.h" | |
47 | #endif | |
48 | ||
063e82dc MK |
49 | #include "../vax/cpu.h" |
50 | #include "../vax/mtpr.h" | |
1a568327 MK |
51 | #include "if_exreg.h" |
52 | #include "if_uba.h" | |
063e82dc MK |
53 | #include "../vaxuba/ubareg.h" |
54 | #include "../vaxuba/ubavar.h" | |
55 | ||
56 | #define DEBUG /* check for "impossible" events */ | |
57 | ||
58 | #define NH2X 4 /* a sufficient number is critical */ | |
59 | #define NX2H 4 /* this is pretty arbitrary */ | |
60 | #define EXWATCHINTVL 10 /* call exwatch() every 10 seconds */ | |
61 | ||
62 | int exprobe(), exattach(), excdint(); | |
63 | struct uba_device *exinfo[NEX]; | |
64 | u_short exstd[] = { 0 }; | |
65 | struct uba_driver exdriver = | |
66 | { exprobe, 0, exattach, 0, exstd, "ex", exinfo }; | |
67 | int exinit(),exoutput(),exioctl(),exreset(),exwatch(); | |
68 | struct ex_msg *exgetcbuf(); | |
69 | ||
70 | /* | |
71 | * Ethernet software status per interface. | |
72 | * | |
73 | * Each interface is referenced by a network interface structure, | |
74 | * xs_if, which the routing code uses to locate the interface. | |
75 | * This structure contains the output queue for the interface, its address, ... | |
76 | * We also have, for each interface, a UBA interface structure, which | |
77 | * contains information about the UNIBUS resources held by the interface: | |
78 | * map registers, buffered data paths, etc. Information is cached in this | |
79 | * structure for use by the if_uba.c routines in running the interface | |
80 | * efficiently. | |
81 | */ | |
82 | struct ex_softc { | |
063e82dc MK |
83 | struct arpcom xs_ac; /* Ethernet common part */ |
84 | #define xs_if xs_ac.ac_if /* network-visible interface */ | |
85 | #define xs_addr xs_ac.ac_enaddr /* hardware Ethernet address */ | |
1a568327 MK |
86 | #ifdef DEBUG |
87 | int xs_wait; | |
88 | #endif | |
063e82dc MK |
89 | struct ifuba xs_ifuba; /* UNIBUS resources */ |
90 | int xs_flags; /* private flags */ | |
91 | #define EX_XPENDING 1 /* xmit rqst pending on EXOS */ | |
92 | #define EX_STATPENDING (1<<1) /* stats rqst pending on EXOS */ | |
0f487295 | 93 | #define EX_RUNNING (1<<2) /* board is running */ |
be85b93a | 94 | #define EX_SETADDR (1<<3) /* physaddr has been changed */ |
063e82dc MK |
95 | struct ex_msg *xs_h2xnext; /* host pointer to request queue */ |
96 | struct ex_msg *xs_x2hnext; /* host pointer to reply queue */ | |
97 | u_long xs_ubaddr; /* map info for structs below */ | |
98 | #define UNIADDR(x) ((u_long)(x)&0x3FFFF) | |
99 | #define P_UNIADDR(x) ((u_long)(x)&0x3FFF0) | |
100 | /* the following structures are always mapped in */ | |
101 | u_short xs_h2xhdr; /* EXOS's request queue header */ | |
102 | u_short xs_x2hhdr; /* EXOS's reply queue header */ | |
103 | struct ex_msg xs_h2xent[NH2X]; /* request msg buffers */ | |
104 | struct ex_msg xs_x2hent[NX2H]; /* reply msg buffers */ | |
105 | struct confmsg xs_cm; /* configuration message */ | |
106 | struct stat_array xs_xsa; /* EXOS writes stats here */ | |
107 | /* end mapped area */ | |
108 | #define INCORE_BASE(p) (((u_long)(&(p)->xs_h2xhdr)) & 0xFFFFFFF0) | |
1090d1e0 KM |
109 | #define RVAL_OFF(unit, n) \ |
110 | ((u_long)(&(ex_softc[unit].n)) - INCORE_BASE(&ex_softc[unit])) | |
111 | #define LVAL_OFF(unit, n) \ | |
112 | ((u_long)(ex_softc[unit].n) - INCORE_BASE(&ex_softc[unit])) | |
113 | #define H2XHDR_OFFSET(unit) RVAL_OFF(unit, xs_h2xhdr) | |
114 | #define X2HHDR_OFFSET(unit) RVAL_OFF(unit, xs_x2hhdr) | |
115 | #define H2XENT_OFFSET(unit) LVAL_OFF(unit, xs_h2xent) | |
116 | #define X2HENT_OFFSET(unit) LVAL_OFF(unit, xs_x2hent) | |
117 | #define CM_OFFSET(unit) RVAL_OFF(unit, xs_cm) | |
118 | #define SA_OFFSET(unit) RVAL_OFF(unit, xs_xsa) | |
119 | #define INCORE_SIZE(unit) RVAL_OFF(unit, xs_end) | |
063e82dc MK |
120 | int xs_end; /* place holder */ |
121 | } ex_softc[NEX]; | |
122 | ||
123 | /* | |
124 | * The following structure is a kludge to store a cvec value | |
125 | * between the time exprobe is called, and exconfig. | |
126 | */ | |
127 | struct ex_cvecs { | |
128 | struct exdevice *xc_csraddr; | |
129 | int xc_cvec; | |
130 | }ex_cvecs[NEX]; | |
131 | ||
132 | int ex_ncall = 0; /* counts calls to exprobe */ | |
133 | ||
134 | exprobe(reg) | |
135 | caddr_t reg; | |
136 | { | |
137 | register int br, cvec; /* r11, r10 value-result */ | |
138 | register struct exdevice *addr = (struct exdevice *)reg; | |
139 | register i; | |
140 | ||
141 | /* | |
142 | * We program the EXOS interrupt vector, like dmf device. | |
143 | */ | |
144 | br = 0x15; | |
145 | cvec = (uba_hd[numuba].uh_lastiv -= 4); | |
146 | #ifdef DEBUG | |
147 | printf("exprobe%d: cvec = %o\n", ex_ncall, cvec); | |
148 | #endif | |
149 | ex_cvecs[ex_ncall].xc_csraddr = addr; | |
150 | ex_cvecs[ex_ncall++].xc_cvec = cvec; | |
151 | /* | |
152 | * Reset EXOS and run self-test (guaranteed to | |
153 | * complete within 2 seconds). | |
154 | */ | |
155 | addr->xd_porta = EX_RESET; | |
156 | i = 1000000; | |
157 | while (((addr->xd_portb & EX_TESTOK) == 0) && --i) | |
158 | ; | |
159 | if ((addr->xd_portb & EX_TESTOK) == 0) { | |
160 | printf("ex: self-test failed\n"); | |
161 | return 0; | |
162 | } | |
7bdbbe60 MK |
163 | #ifdef lint |
164 | br = br; | |
165 | #endif | |
063e82dc MK |
166 | return (sizeof(struct exdevice)); |
167 | } | |
168 | ||
169 | /* | |
170 | * Interface exists: make available by filling in network interface | |
171 | * record. System will initialize the interface when it is ready | |
172 | * to accept packets. Board is temporarily configured and issues | |
173 | * a NET_ADDRS command, only to get the Ethernet address. | |
174 | */ | |
175 | exattach(ui) | |
176 | struct uba_device *ui; | |
177 | { | |
178 | register struct ex_softc *xs = &ex_softc[ui->ui_unit]; | |
179 | register struct ifnet *ifp = &xs->xs_if; | |
180 | register struct exdevice *addr = (struct exdevice *)ui->ui_addr; | |
063e82dc | 181 | register struct ex_msg *bp; |
1090d1e0 | 182 | int unit = ui->ui_unit; |
063e82dc MK |
183 | ifp->if_unit = ui->ui_unit; |
184 | ifp->if_name = "ex"; | |
185 | ifp->if_mtu = ETHERMTU; | |
186 | ||
187 | /* | |
188 | * Temporarily map queues in order to configure EXOS | |
189 | */ | |
1090d1e0 KM |
190 | xs->xs_ubaddr = uballoc(ui->ui_ubanum, INCORE_BASE(xs), |
191 | INCORE_SIZE(unit), 0); | |
063e82dc MK |
192 | exconfig(ui, 0); /* without interrupts */ |
193 | if (xs->xs_cm.cm_cc) goto badconf; | |
194 | ||
195 | bp = exgetcbuf(xs); | |
196 | bp->mb_rqst = LLNET_ADDRS; | |
197 | bp->mb_na.na_mask = READ_OBJ; | |
198 | bp->mb_na.na_slot = PHYSSLOT; | |
199 | bp->mb_status |= MH_EXOS; | |
200 | addr->xd_portb = EX_NTRUPT; | |
201 | bp = xs->xs_x2hnext; | |
202 | while ((bp->mb_status & MH_OWNER) == MH_EXOS) /* poll for reply */ | |
203 | ; | |
87c6a528 MK |
204 | printf("ex%d: HW %c.%c, NX %c.%c, hardware address %s\n", |
205 | ui->ui_unit, ether_sprintf(bp->mb_na.na_addrs), | |
063e82dc | 206 | xs->xs_cm.cm_vc[2], xs->xs_cm.cm_vc[3], |
87c6a528 | 207 | xs->xs_cm.cm_vc[0], xs->xs_cm.cm_vc[1]); |
063e82dc MK |
208 | bcopy((caddr_t)bp->mb_na.na_addrs, (caddr_t)xs->xs_addr, |
209 | sizeof (xs->xs_addr)); | |
210 | ||
063e82dc MK |
211 | ifp->if_init = exinit; |
212 | ifp->if_output = exoutput; | |
213 | ifp->if_ioctl = exioctl; | |
214 | ifp->if_reset = exreset; | |
1a568327 | 215 | ifp->if_flags = IFF_BROADCAST; |
063e82dc MK |
216 | xs->xs_ifuba.ifu_flags = UBA_CANTWAIT; |
217 | if_attach(ifp); | |
218 | badconf: | |
219 | ubarelse(ui->ui_ubanum, &xs->xs_ubaddr); | |
220 | } | |
221 | ||
222 | /* | |
223 | * Reset of interface after UNIBUS reset. | |
224 | * If interface is on specified uba, reset its state. | |
225 | */ | |
226 | exreset(unit, uban) | |
227 | int unit, uban; | |
228 | { | |
229 | register struct uba_device *ui; | |
230 | ||
231 | if (unit >= NEX || (ui = exinfo[unit]) == 0 || ui->ui_alive == 0 || | |
232 | ui->ui_ubanum != uban) | |
233 | return; | |
234 | printf(" ex%d", unit); | |
1a568327 | 235 | ex_softc[unit].xs_if.if_flags &= ~IFF_RUNNING; |
0f487295 | 236 | ex_softc[unit].xs_flags &= ~EX_RUNNING; |
063e82dc MK |
237 | exinit(unit); |
238 | } | |
239 | ||
240 | /* | |
241 | * Initialization of interface; clear recorded pending | |
242 | * operations, and reinitialize UNIBUS usage. | |
243 | * Called at boot time (with interrupts disabled?), | |
244 | * and at ifconfig time via exioctl, with interrupts disabled. | |
245 | */ | |
246 | exinit(unit) | |
247 | int unit; | |
248 | { | |
249 | register struct ex_softc *xs = &ex_softc[unit]; | |
250 | register struct uba_device *ui = exinfo[unit]; | |
251 | register struct exdevice *addr = (struct exdevice *)ui->ui_addr; | |
252 | register struct ifnet *ifp = &xs->xs_if; | |
063e82dc MK |
253 | register struct ex_msg *bp; |
254 | int s; | |
255 | ||
1a568327 MK |
256 | /* not yet, if address still unknown */ |
257 | if (ifp->if_addrlist == (struct ifaddr *)0) | |
063e82dc | 258 | return; |
0f487295 | 259 | if (xs->xs_flags & EX_RUNNING) |
063e82dc | 260 | return; |
0f487295 MK |
261 | |
262 | if ((ifp->if_flags & IFF_RUNNING) == 0) { | |
263 | if (if_ubainit(&xs->xs_ifuba, ui->ui_ubanum, | |
264 | sizeof (struct ether_header), | |
265 | (int)btoc(EXMAXRBUF-sizeof(struct ether_header))) == 0) { | |
266 | printf("ex%d: can't initialize\n", unit); | |
267 | xs->xs_if.if_flags &= ~IFF_UP; | |
268 | return; | |
269 | } | |
270 | xs->xs_ubaddr = uballoc(ui->ui_ubanum, INCORE_BASE(xs), | |
1090d1e0 | 271 | INCORE_SIZE(unit), 0); |
063e82dc | 272 | } |
063e82dc MK |
273 | exconfig(ui, 4); /* with vectored interrupts*/ |
274 | /* | |
275 | * Put EXOS on the Ethernet, using NET_MODE command | |
276 | */ | |
277 | bp = exgetcbuf(xs); | |
278 | bp->mb_rqst = LLNET_MODE; | |
279 | bp->mb_nm.nm_mask = WRITE_OBJ; | |
280 | bp->mb_nm.nm_optn = 0; | |
281 | bp->mb_nm.nm_mode = MODE_PERF; | |
282 | bp->mb_status |= MH_EXOS; | |
283 | addr->xd_portb = EX_NTRUPT; | |
284 | bp = xs->xs_x2hnext; | |
285 | while ((bp->mb_status & MH_OWNER) == MH_EXOS) /* poll for reply */ | |
286 | ; | |
287 | bp->mb_length = MBDATALEN; | |
288 | bp->mb_status |= MH_EXOS; /* free up buffer */ | |
289 | addr->xd_portb = EX_NTRUPT; /* tell EXOS about it */ | |
290 | xs->xs_x2hnext = xs->xs_x2hnext->mb_next; | |
291 | ||
292 | ifp->if_watchdog = exwatch; | |
293 | ifp->if_timer = EXWATCHINTVL; | |
294 | s = splimp(); /* are interrupts always disabled here, anyway? */ | |
295 | exhangrcv(unit); /* hang receive request */ | |
1a568327 | 296 | xs->xs_if.if_flags |= IFF_RUNNING; |
0f487295 | 297 | xs->xs_flags |= EX_RUNNING; |
be85b93a KS |
298 | if (xs->xs_flags & EX_SETADDR) |
299 | ex_setaddr(0, unit); | |
300 | exstart(unit); /* start transmits */ | |
063e82dc | 301 | splx(s); |
063e82dc MK |
302 | } |
303 | ||
304 | /* | |
305 | * Reset, test, and configure EXOS. This routine assumes | |
306 | * that message queues, etc. have already been mapped into | |
307 | * the UBA. It is called by exinit, and should also be | |
308 | * callable by exattach. | |
309 | */ | |
310 | exconfig(ui, itype) | |
311 | struct uba_device *ui; | |
312 | int itype; | |
313 | { | |
314 | register int unit = ui->ui_unit; | |
315 | register struct ex_softc *xs = &ex_softc[unit]; | |
316 | register struct exdevice *addr = (struct exdevice *) ui->ui_addr; | |
317 | register struct confmsg *cm = &xs->xs_cm; | |
318 | register struct ex_msg *bp; | |
319 | int i; | |
320 | u_long shiftreg; | |
321 | ||
322 | xs->xs_flags = 0; | |
323 | /* | |
324 | * Reset EXOS, wait for self-test to complete | |
325 | */ | |
326 | addr->xd_porta = EX_RESET; | |
327 | while ((addr->xd_portb & EX_TESTOK) == 0) | |
328 | ; | |
329 | /* | |
330 | * Set up configuration message. | |
331 | */ | |
332 | cm->cm_1rsrv = 1; | |
333 | cm->cm_cc = 0xFF; | |
334 | cm->cm_opmode = 0; /* link-level controller mode */ | |
335 | cm->cm_dfo = 0x0101; /* enable host data order conversion */ | |
336 | cm->cm_dcn1 = 1; | |
337 | cm->cm_2rsrv[0] = | |
338 | cm->cm_2rsrv[1] = 0; | |
339 | cm->cm_ham = 3; /* absolute address mode */ | |
340 | cm->cm_3rsrv = 0; | |
341 | cm->cm_mapsiz = 0; | |
342 | cm->cm_byteptrn[0] = 0x01; /* EXOS deduces data order of host */ | |
343 | cm->cm_byteptrn[1] = 0x03; /* by looking at this pattern */ | |
344 | cm->cm_byteptrn[2] = 0x07; | |
345 | cm->cm_byteptrn[3] = 0x0F; | |
346 | cm->cm_wordptrn[0] = 0x0103; | |
347 | cm->cm_wordptrn[1] = 0x070F; | |
348 | cm->cm_lwordptrn = 0x0103070F; | |
349 | for (i=0; i<20; i++) cm->cm_rsrvd[i] = 0; | |
350 | cm->cm_mba = 0xFFFFFFFF; | |
351 | cm->cm_nproc = 0xFF; | |
352 | cm->cm_nmbox = 0xFF; | |
353 | cm->cm_nmcast = 0xFF; | |
354 | cm->cm_nhost = 1; | |
355 | cm->cm_h2xba = P_UNIADDR(xs->xs_ubaddr); | |
1090d1e0 | 356 | cm->cm_h2xhdr = H2XHDR_OFFSET(unit); |
063e82dc MK |
357 | cm->cm_h2xtyp = 0; /* should never wait for rqst buffer */ |
358 | cm->cm_x2hba = cm->cm_h2xba; | |
1090d1e0 | 359 | cm->cm_x2hhdr = X2HHDR_OFFSET(unit); |
063e82dc MK |
360 | cm->cm_x2htyp = itype; /* 0 for none, 4 for vectored */ |
361 | for (i=0; (addr != ex_cvecs[i].xc_csraddr); i++) | |
362 | #ifdef DEBUG | |
363 | if (i >= NEX) | |
364 | panic("ex: matching csr address not found"); | |
365 | #endif | |
366 | ; | |
367 | cm->cm_x2haddr = ex_cvecs[i].xc_cvec; /* stashed here by exprobe */ | |
368 | /* | |
369 | * Set up message queues and headers. | |
370 | * First the request queue. | |
371 | */ | |
372 | for (bp = &xs->xs_h2xent[0]; bp < &xs->xs_h2xent[NH2X]; bp++) { | |
373 | bp->mb_link = (u_short)((char *)(bp+1)-INCORE_BASE(xs)); | |
374 | bp->mb_rsrv = 0; | |
375 | bp->mb_length = MBDATALEN; | |
376 | bp->mb_status = MH_HOST; | |
377 | bp->mb_next = bp+1; | |
378 | } | |
379 | xs->xs_h2xhdr = | |
380 | xs->xs_h2xent[NH2X-1].mb_link = | |
1090d1e0 | 381 | (u_short)H2XENT_OFFSET(unit); |
063e82dc MK |
382 | xs->xs_h2xnext = |
383 | xs->xs_h2xent[NH2X-1].mb_next = | |
384 | xs->xs_h2xent; | |
385 | ||
386 | /* Now the reply queue. */ | |
387 | for (bp = &xs->xs_x2hent[0]; bp < &xs->xs_x2hent[NX2H]; bp++) { | |
388 | bp->mb_link = (u_short)((char *)(bp+1)-INCORE_BASE(xs)); | |
389 | bp->mb_rsrv = 0; | |
390 | bp->mb_length = MBDATALEN; | |
391 | bp->mb_status = MH_EXOS; | |
392 | bp->mb_next = bp+1; | |
393 | } | |
394 | xs->xs_x2hhdr = | |
395 | xs->xs_x2hent[NX2H-1].mb_link = | |
1090d1e0 | 396 | (u_short)X2HENT_OFFSET(unit); |
063e82dc MK |
397 | xs->xs_x2hnext = |
398 | xs->xs_x2hent[NX2H-1].mb_next = | |
399 | xs->xs_x2hent; | |
400 | ||
401 | /* | |
402 | * Write config msg address to EXOS and wait for | |
403 | * configuration to complete (guaranteed response | |
404 | * within 2 seconds). | |
405 | */ | |
406 | shiftreg = (u_long)0x0000FFFF; | |
407 | for (i = 0; i < 8; i++) { | |
408 | if (i == 4) | |
1090d1e0 | 409 | shiftreg = P_UNIADDR(xs->xs_ubaddr) + CM_OFFSET(unit); |
063e82dc MK |
410 | while (addr->xd_portb & EX_UNREADY) |
411 | ; | |
412 | addr->xd_portb = (u_char)(shiftreg & 0xFF); | |
413 | shiftreg >>= 8; | |
414 | } | |
415 | for (i = 1000000; (cm->cm_cc == 0xFF) && i; --i); | |
416 | if (cm->cm_cc) | |
417 | printf("ex%d: configuration failed; cc = %x\n", | |
418 | unit, cm->cm_cc); | |
419 | } | |
420 | ||
421 | /* | |
422 | * Start or re-start output on interface. | |
423 | * Get another datagram to send off of the interface queue, | |
424 | * and map it to the interface before starting the output. | |
425 | * This routine is called by exinit(), exoutput(), and excdint(). | |
426 | * In all cases, interrupts by EXOS are disabled. | |
427 | */ | |
428 | exstart(unit) | |
429 | int unit; | |
430 | { | |
431 | struct uba_device *ui = exinfo[unit]; | |
432 | register struct ex_softc *xs = &ex_softc[unit]; | |
433 | register struct exdevice *addr = (struct exdevice *)ui->ui_addr; | |
434 | register struct ex_msg *bp; | |
435 | struct mbuf *m; | |
436 | int len; | |
437 | ||
438 | #ifdef DEBUG | |
439 | if (xs->xs_flags & EX_XPENDING) | |
440 | panic("exstart(): xmit still pending"); | |
441 | #endif | |
442 | IF_DEQUEUE(&xs->xs_if.if_snd, m); | |
443 | if (m == 0) | |
444 | return; | |
445 | len = if_wubaput(&xs->xs_ifuba, m); | |
446 | if (len - sizeof(struct ether_header) < ETHERMIN) | |
447 | len = ETHERMIN + sizeof(struct ether_header); | |
448 | /* | |
449 | * Place a transmit request. | |
450 | */ | |
451 | bp = exgetcbuf(xs); | |
452 | bp->mb_rqst = LLRTRANSMIT; | |
453 | bp->mb_et.et_nblock = 1; | |
454 | bp->mb_et.et_blks[0].bb_len = (u_short)len; | |
455 | *(u_long *)bp->mb_et.et_blks[0].bb_addr = | |
456 | UNIADDR(xs->xs_ifuba.ifu_w.ifrw_info); | |
457 | xs->xs_flags |= EX_XPENDING; | |
458 | bp->mb_status |= MH_EXOS; | |
459 | addr->xd_portb = EX_NTRUPT; | |
460 | } | |
461 | ||
462 | /* | |
463 | * Command done interrupt. | |
464 | */ | |
465 | excdint(unit) | |
466 | int unit; | |
467 | { | |
468 | register struct ex_softc *xs = &ex_softc[unit]; | |
469 | register struct ex_msg *bp = xs->xs_x2hnext; | |
470 | struct uba_device *ui = exinfo[unit]; | |
471 | struct exdevice *addr = (struct exdevice *)ui->ui_addr; | |
472 | ||
473 | while ((bp->mb_status & MH_OWNER) == MH_HOST) { | |
474 | switch (bp->mb_rqst) { | |
475 | case LLRECEIVE: | |
476 | exrecv(unit, bp); | |
477 | exhangrcv(unit); | |
478 | break; | |
479 | case LLRTRANSMIT: | |
480 | #ifdef DEBUG | |
481 | if ((xs->xs_flags & EX_XPENDING) == 0) | |
482 | panic("exxmit: no xmit pending"); | |
483 | #endif | |
484 | xs->xs_flags &= ~EX_XPENDING; | |
485 | xs->xs_if.if_opackets++; | |
486 | if (bp->mb_rply == LL_OK) { | |
487 | ; | |
488 | } else if (bp->mb_rply & LLXM_1RTRY) { | |
489 | xs->xs_if.if_collisions++; | |
490 | } else if (bp->mb_rply & LLXM_RTRYS) { | |
491 | xs->xs_if.if_collisions += 2; /* guess */ | |
492 | } else if (bp->mb_rply & LLXM_ERROR) { | |
493 | xs->xs_if.if_oerrors++; | |
494 | printf("ex%d: transmit error=%b\n", | |
495 | unit, bp->mb_rply, XMIT_BITS); | |
496 | } | |
497 | if (xs->xs_ifuba.ifu_xtofree) { | |
498 | m_freem(xs->xs_ifuba.ifu_xtofree); | |
499 | xs->xs_ifuba.ifu_xtofree = 0; | |
500 | } | |
501 | exstart(unit); | |
502 | break; | |
503 | case LLNET_STSTCS: | |
504 | xs->xs_if.if_ierrors = xs->xs_xsa.sa_crc; | |
505 | xs->xs_flags &= ~EX_STATPENDING; | |
506 | break; | |
be85b93a KS |
507 | case LLNET_ADDRS: |
508 | case LLNET_RECV: | |
509 | break; | |
063e82dc MK |
510 | #ifdef DEBUG |
511 | default: | |
512 | panic("ex%d: unknown reply"); | |
513 | #endif | |
514 | } /* end of switch */ | |
515 | bp->mb_length = MBDATALEN; | |
516 | bp->mb_status |= MH_EXOS; /* free up buffer */ | |
517 | addr->xd_portb = EX_NTRUPT; /* tell EXOS about it */ | |
518 | bp = xs->xs_x2hnext = xs->xs_x2hnext->mb_next; | |
519 | } | |
520 | } | |
521 | ||
522 | /* | |
523 | * Get a request buffer, fill in standard values, advance pointer. | |
524 | */ | |
525 | struct ex_msg * | |
526 | exgetcbuf(xs) | |
527 | struct ex_softc *xs; | |
528 | { | |
529 | register struct ex_msg *bp = xs->xs_h2xnext; | |
530 | ||
531 | #ifdef DEBUG | |
532 | if ((bp->mb_status & MH_OWNER) == MH_EXOS) | |
533 | panic("exgetcbuf(): EXOS owns message buffer"); | |
534 | #endif | |
535 | bp->mb_1rsrv = 0; | |
536 | bp->mb_length = MBDATALEN; | |
537 | xs->xs_h2xnext = xs->xs_h2xnext->mb_next; | |
538 | return bp; | |
539 | } | |
540 | ||
541 | /* | |
542 | * Process Ethernet receive completion: | |
543 | * If input error just drop packet. | |
544 | * Otherwise purge input buffered data path and examine | |
545 | * packet to determine type. If can't determine length | |
546 | * from type, then have to drop packet. Otherwise decapsulate | |
547 | * packet based on type and pass to type-specific higher-level | |
548 | * input routine. | |
549 | */ | |
550 | exrecv(unit, bp) | |
551 | int unit; | |
552 | register struct ex_msg *bp; | |
553 | { | |
554 | register struct ex_softc *xs = &ex_softc[unit]; | |
555 | register struct ether_header *eh; | |
556 | struct mbuf *m; | |
557 | register int len, off, resid; | |
558 | register struct ifqueue *inq; | |
559 | ||
560 | xs->xs_if.if_ipackets++; | |
561 | len = bp->mb_er.er_blks[0].bb_len - sizeof(struct ether_header) - 4; | |
562 | if (bp->mb_rply != LL_OK) { | |
563 | xs->xs_if.if_ierrors++; | |
564 | printf("ex%d: receive error=%b\n", | |
565 | unit, bp->mb_rply, RECV_BITS); | |
566 | return; | |
567 | } | |
568 | eh = (struct ether_header *)(xs->xs_ifuba.ifu_r.ifrw_addr); | |
569 | ||
570 | /* | |
1a568327 | 571 | * Deal with trailer protocol: if type is trailer |
063e82dc MK |
572 | * get true type from first 16-bit word past data. |
573 | * Remember that type was trailer by setting off. | |
574 | */ | |
575 | eh->ether_type = ntohs((u_short)eh->ether_type); | |
576 | #define exdataaddr(eh, off, type) ((type)(((caddr_t)((eh)+1)+(off)))) | |
1a568327 MK |
577 | if (eh->ether_type >= ETHERTYPE_TRAIL && |
578 | eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { | |
579 | off = (eh->ether_type - ETHERTYPE_TRAIL) * 512; | |
063e82dc MK |
580 | if (off >= ETHERMTU) |
581 | return; /* sanity */ | |
582 | eh->ether_type = ntohs(*exdataaddr(eh, off, u_short *)); | |
583 | resid = ntohs(*(exdataaddr(eh, off+2, u_short *))); | |
584 | if (off + resid > len) | |
585 | return; /* sanity */ | |
586 | len = off + resid; | |
587 | } else | |
588 | off = 0; | |
589 | if (len == 0) | |
590 | return; | |
591 | ||
592 | /* | |
593 | * Pull packet off interface. Off is nonzero if packet | |
594 | * has trailing header; if_rubaget will then force this header | |
595 | * information to be at the front, but we still have to drop | |
596 | * the type and length which are at the front of any trailer data. | |
597 | */ | |
7bdbbe60 | 598 | m = if_rubaget(&xs->xs_ifuba, len, off, &xs->xs_if); |
063e82dc MK |
599 | if (m == 0) |
600 | return; | |
601 | if (off) { | |
7bdbbe60 MK |
602 | struct ifnet *ifp; |
603 | ||
604 | ifp = *(mtod(m, struct ifnet **)); | |
063e82dc MK |
605 | m->m_off += 2 * sizeof (u_short); |
606 | m->m_len -= 2 * sizeof (u_short); | |
7bdbbe60 | 607 | *(mtod(m, struct ifnet **)) = ifp; |
063e82dc MK |
608 | } |
609 | switch (eh->ether_type) { | |
610 | ||
611 | #ifdef INET | |
1a568327 | 612 | case ETHERTYPE_IP: |
063e82dc MK |
613 | schednetisr(NETISR_IP); /* is this necessary */ |
614 | inq = &ipintrq; | |
615 | break; | |
616 | ||
1a568327 | 617 | case ETHERTYPE_ARP: |
063e82dc MK |
618 | arpinput(&xs->xs_ac, m); |
619 | return; | |
f2803948 KS |
620 | #endif |
621 | #ifdef NS | |
622 | case ETHERTYPE_NS: | |
623 | schednetisr(NETISR_NS); | |
624 | inq = &nsintrq; | |
625 | break; | |
626 | ||
063e82dc MK |
627 | #endif |
628 | default: | |
629 | m_freem(m); | |
630 | return; | |
631 | } | |
632 | ||
633 | if (IF_QFULL(inq)) { | |
634 | IF_DROP(inq); | |
635 | m_freem(m); | |
636 | return; | |
637 | } | |
638 | IF_ENQUEUE(inq, m); | |
639 | } | |
640 | ||
641 | /* | |
642 | * Send receive request to EXOS. | |
643 | * This routine is called by exinit and excdint, | |
644 | * with interrupts disabled in both cases. | |
645 | */ | |
646 | exhangrcv(unit) | |
647 | int unit; | |
648 | { | |
649 | register struct ex_softc *xs = &ex_softc[unit]; | |
650 | register struct ex_msg *bp = exgetcbuf(xs); | |
651 | struct exdevice *addr = (struct exdevice *)exinfo[unit]->ui_addr; | |
652 | ||
653 | bp->mb_rqst = LLRECEIVE; | |
654 | bp->mb_er.er_nblock = 1; | |
655 | bp->mb_er.er_blks[0].bb_len = EXMAXRBUF; | |
656 | *(u_long *)bp->mb_er.er_blks[0].bb_addr = | |
657 | UNIADDR(xs->xs_ifuba.ifu_r.ifrw_info); | |
658 | bp->mb_status |= MH_EXOS; | |
659 | addr->xd_portb = EX_NTRUPT; | |
660 | } | |
661 | ||
662 | /* | |
663 | * Ethernet output routine. | |
664 | * Encapsulate a packet of type family for the local net. | |
665 | * Use trailer local net encapsulation if enough data in first | |
666 | * packet leaves a multiple of 512 bytes of data in remainder. | |
667 | */ | |
668 | exoutput(ifp, m0, dst) | |
669 | register struct ifnet *ifp; | |
670 | register struct mbuf *m0; | |
671 | struct sockaddr *dst; | |
672 | { | |
673 | int type, s, error; | |
674 | u_char edst[6]; | |
675 | struct in_addr idst; | |
676 | register struct ex_softc *xs = &ex_softc[ifp->if_unit]; | |
677 | register struct mbuf *m = m0; | |
678 | register struct ether_header *eh; | |
679 | register int off; | |
87c6a528 | 680 | int usetrailers; |
063e82dc | 681 | |
0f487295 MK |
682 | if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { |
683 | error = ENETDOWN; | |
684 | goto bad; | |
685 | } | |
063e82dc MK |
686 | switch (dst->sa_family) { |
687 | ||
688 | #ifdef INET | |
689 | case AF_INET: | |
690 | idst = ((struct sockaddr_in *)dst)->sin_addr; | |
87c6a528 | 691 | if (!arpresolve(&xs->xs_ac, m, &idst, edst, &usetrailers)) |
063e82dc MK |
692 | return (0); /* if not yet resolved */ |
693 | off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; | |
87c6a528 | 694 | if (usetrailers && off > 0 && (off & 0x1ff) == 0 && |
063e82dc | 695 | m->m_off >= MMINOFF + 2 * sizeof (u_short)) { |
1a568327 | 696 | type = ETHERTYPE_TRAIL + (off>>9); |
063e82dc MK |
697 | m->m_off -= 2 * sizeof (u_short); |
698 | m->m_len += 2 * sizeof (u_short); | |
1a568327 | 699 | *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP); |
063e82dc MK |
700 | *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len); |
701 | goto gottrailertype; | |
702 | } | |
1a568327 | 703 | type = ETHERTYPE_IP; |
063e82dc MK |
704 | off = 0; |
705 | goto gottype; | |
706 | #endif | |
f2803948 KS |
707 | #ifdef NS |
708 | case AF_NS: | |
709 | type = ETHERTYPE_NS; | |
710 | bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host), | |
711 | (caddr_t)edst, sizeof (edst)); | |
712 | off = 0; | |
713 | goto gottype; | |
714 | #endif | |
063e82dc MK |
715 | |
716 | case AF_UNSPEC: | |
717 | eh = (struct ether_header *)dst->sa_data; | |
718 | bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst)); | |
719 | type = eh->ether_type; | |
720 | goto gottype; | |
721 | ||
722 | default: | |
723 | printf("ex%d: can't handle af%d\n", ifp->if_unit, | |
724 | dst->sa_family); | |
725 | error = EAFNOSUPPORT; | |
726 | goto bad; | |
727 | } | |
728 | ||
729 | gottrailertype: | |
730 | /* | |
731 | * Packet to be sent as trailer: move first packet | |
732 | * (control information) to end of chain. | |
733 | */ | |
734 | while (m->m_next) | |
735 | m = m->m_next; | |
736 | m->m_next = m0; | |
737 | m = m0->m_next; | |
738 | m0->m_next = 0; | |
739 | m0 = m; | |
740 | ||
741 | gottype: | |
742 | /* | |
743 | * Add local net header. If no space in first mbuf, | |
744 | * allocate another. | |
745 | */ | |
746 | if (m->m_off > MMAXOFF || | |
747 | MMINOFF + sizeof (struct ether_header) > m->m_off) { | |
748 | m = m_get(M_DONTWAIT, MT_HEADER); | |
749 | if (m == 0) { | |
750 | error = ENOBUFS; | |
751 | goto bad; | |
752 | } | |
753 | m->m_next = m0; | |
754 | m->m_off = MMINOFF; | |
755 | m->m_len = sizeof (struct ether_header); | |
756 | } else { | |
757 | m->m_off -= sizeof (struct ether_header); | |
758 | m->m_len += sizeof (struct ether_header); | |
759 | } | |
760 | eh = mtod(m, struct ether_header *); | |
761 | eh->ether_type = htons((u_short)type); | |
762 | bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst)); | |
763 | bcopy((caddr_t)xs->xs_addr, (caddr_t)eh->ether_shost, 6); | |
764 | ||
765 | /* | |
766 | * Queue message on interface, and start output if interface | |
767 | * not yet active. | |
768 | */ | |
769 | s = splimp(); | |
770 | if (IF_QFULL(&ifp->if_snd)) { | |
771 | IF_DROP(&ifp->if_snd); | |
772 | splx(s); | |
773 | m_freem(m); | |
774 | return (ENOBUFS); | |
775 | } | |
776 | IF_ENQUEUE(&ifp->if_snd, m); | |
777 | /* | |
778 | * If transmit request not already pending, then | |
779 | * kick the back end. | |
780 | */ | |
781 | if ((xs->xs_flags & EX_XPENDING) == 0) { | |
782 | exstart(ifp->if_unit); | |
783 | } | |
784 | #ifdef DEBUG | |
785 | else { | |
786 | xs->xs_wait++; | |
787 | } | |
788 | #endif | |
789 | splx(s); | |
790 | return (0); | |
791 | ||
792 | bad: | |
793 | m_freem(m0); | |
794 | return (error); | |
795 | } | |
796 | ||
797 | /* | |
798 | * Watchdog routine - place stats request to EXOS | |
799 | * (This could be dispensed with, if you don't care | |
800 | * about the if_ierrors count, or are willing to receive | |
801 | * bad packets in order to derive it.) | |
802 | */ | |
803 | exwatch(unit) | |
804 | int unit; | |
805 | { | |
806 | struct uba_device *ui = exinfo[unit]; | |
807 | struct exdevice *addr = (struct exdevice *)ui->ui_addr; | |
808 | register struct ex_softc *xs = &ex_softc[unit]; | |
809 | register struct ex_msg *bp; | |
810 | int s = splimp(); | |
811 | ||
812 | if (xs->xs_flags & EX_STATPENDING) goto exspnd; | |
813 | bp = exgetcbuf(xs); | |
814 | xs->xs_flags |= EX_STATPENDING; | |
815 | bp->mb_rqst = LLNET_STSTCS; | |
816 | bp->mb_ns.ns_mask = READ_OBJ; | |
817 | bp->mb_ns.ns_rsrv = 0; | |
818 | bp->mb_ns.ns_nobj = 8; /* read all 8 stats objects */ | |
819 | bp->mb_ns.ns_xobj = 0; /* starting with the 1st one */ | |
1090d1e0 | 820 | bp->mb_ns.ns_bufp = P_UNIADDR(xs->xs_ubaddr) + SA_OFFSET(unit); |
063e82dc MK |
821 | bp->mb_status |= MH_EXOS; |
822 | addr->xd_portb = EX_NTRUPT; | |
823 | exspnd: | |
824 | splx(s); | |
825 | xs->xs_if.if_timer = EXWATCHINTVL; | |
826 | } | |
827 | ||
828 | /* | |
829 | * Process an ioctl request. | |
830 | */ | |
831 | exioctl(ifp, cmd, data) | |
832 | register struct ifnet *ifp; | |
833 | int cmd; | |
834 | caddr_t data; | |
835 | { | |
1a568327 | 836 | register struct ifaddr *ifa = (struct ifaddr *)data; |
0f487295 | 837 | register struct ex_softc *xs = &ex_softc[ifp->if_unit]; |
063e82dc MK |
838 | int s = splimp(), error = 0; |
839 | ||
840 | switch (cmd) { | |
841 | ||
842 | case SIOCSIFADDR: | |
1a568327 | 843 | ifp->if_flags |= IFF_UP; |
7bdbbe60 | 844 | exinit(ifp->if_unit); |
1a568327 MK |
845 | |
846 | switch (ifa->ifa_addr.sa_family) { | |
f2803948 | 847 | #ifdef INET |
1a568327 MK |
848 | case AF_INET: |
849 | ((struct arpcom *)ifp)->ac_ipaddr = | |
850 | IA_SIN(ifa)->sin_addr; | |
851 | arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); | |
852 | break; | |
f2803948 KS |
853 | #endif |
854 | #ifdef NS | |
855 | case AF_NS: | |
be85b93a KS |
856 | { |
857 | register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); | |
858 | ||
859 | if (ns_nullhost(*ina)) | |
860 | ina->x_host = *(union ns_host *)(xs->xs_addr); | |
861 | else | |
862 | ex_setaddr(ina->x_host.c_host,ifp->if_unit); | |
f2803948 | 863 | break; |
be85b93a | 864 | } |
f2803948 | 865 | #endif |
1a568327 | 866 | } |
063e82dc MK |
867 | break; |
868 | ||
0f487295 MK |
869 | case SIOCSIFFLAGS: |
870 | if ((ifp->if_flags & IFF_UP) == 0 && | |
871 | xs->xs_flags & EX_RUNNING) { | |
872 | ((struct exdevice *) | |
873 | (exinfo[ifp->if_unit]->ui_addr))->xd_porta = EX_RESET; | |
874 | xs->xs_flags &= ~EX_RUNNING; | |
875 | } else if (ifp->if_flags & IFF_UP && | |
876 | (xs->xs_flags & EX_RUNNING) == 0) | |
877 | exinit(ifp->if_unit); | |
878 | break; | |
879 | ||
063e82dc MK |
880 | default: |
881 | error = EINVAL; | |
882 | } | |
883 | splx(s); | |
884 | return (error); | |
885 | } | |
be85b93a KS |
886 | |
887 | /* | |
888 | * set ethernet address for unit | |
889 | */ | |
890 | ex_setaddr(physaddr, unit) | |
891 | u_char *physaddr; | |
892 | int unit; | |
893 | { | |
894 | register struct ex_softc *xs = &ex_softc[unit]; | |
895 | struct uba_device *ui = exinfo[unit]; | |
896 | register struct exdevice *addr= (struct exdevice *)ui->ui_addr; | |
897 | register struct ifnet *ifp = &xs->xs_if; | |
898 | register struct ex_msg *bp; | |
899 | ||
900 | if (physaddr) { | |
901 | xs->xs_flags |= EX_SETADDR; | |
902 | bcopy((caddr_t)physaddr, (caddr_t)xs->xs_addr, 6); | |
903 | } | |
904 | if (! (xs->xs_flags & EX_RUNNING)) | |
905 | return; | |
906 | bp = exgetcbuf(xs); | |
907 | bp->mb_rqst = LLNET_ADDRS; | |
908 | bp->mb_na.na_mask = READ_OBJ|WRITE_OBJ; | |
909 | bp->mb_na.na_slot = PHYSSLOT; | |
910 | bcopy(xs->xs_addr, bp->mb_na.na_addrs, 6); | |
911 | bp->mb_status |= MH_EXOS; | |
912 | addr->xd_portb = EX_NTRUPT; | |
913 | bp = xs->xs_x2hnext; | |
914 | while ((bp->mb_status & MH_OWNER) == MH_EXOS) /* poll for reply */ | |
915 | ; | |
87c6a528 MK |
916 | #ifdef DEBUG |
917 | printf("ex%d: reset addr %s\n", ui->ui_unit, | |
918 | ether_sprintf(bp->mb_na.na_addrs)); | |
919 | #endif | |
be85b93a KS |
920 | /* |
921 | * Now, re-enable reception on phys slot. | |
922 | */ | |
923 | bp = exgetcbuf(xs); | |
924 | bp->mb_rqst = LLNET_RECV; | |
925 | bp->mb_nr.nr_mask = ENABLE_RCV|READ_OBJ|WRITE_OBJ; | |
926 | bp->mb_nr.nr_slot = PHYSSLOT; | |
927 | bp->mb_status |= MH_EXOS; | |
928 | addr->xd_portb = EX_NTRUPT; | |
929 | bp = xs->xs_x2hnext; | |
930 | while ((bp->mb_status & MH_OWNER) == MH_EXOS) /* poll for reply */ | |
931 | ; | |
932 | } | |
9a0b0c74 | 933 | #endif |