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