Commit | Line | Data |
---|---|---|
19df187f KS |
1 | /* |
2 | * Copyright (c) 1989 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * This code is derived from software contributed to Berkeley by | |
6 | * Excelan Inc. | |
7 | * | |
8 | * Redistribution and use in source and binary forms are permitted | |
9 | * provided that the above copyright notice and this paragraph are | |
10 | * duplicated in all such forms and that any documentation, | |
11 | * advertising materials, and other materials related to such | |
12 | * distribution and use acknowledge that the software was developed | |
13 | * by the University of California, Berkeley. The name of the | |
14 | * University may not be used to endorse or promote products derived | |
15 | * from this software without specific prior written permission. | |
16 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
17 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
19 | * | |
385e8f24 | 20 | * @(#)if_ex.c 7.2 (Berkeley) %G% |
19df187f KS |
21 | */ |
22 | ||
23 | #include "ex.h" | |
24 | ||
25 | #if NEX > 0 | |
26 | ||
27 | /* | |
28 | * Excelan EXOS 202(VME) & 203(QBUS) Link Level Ethernet Interface Drivers | |
29 | */ | |
30 | #include "param.h" | |
31 | #include "systm.h" | |
32 | #include "mbuf.h" | |
33 | #include "buf.h" | |
34 | #include "protosw.h" | |
35 | #include "socket.h" | |
36 | #include "vmmac.h" | |
37 | #include "ioctl.h" | |
38 | #include "errno.h" | |
39 | #include "vmparam.h" | |
40 | #include "syslog.h" | |
41 | #include "uio.h" | |
42 | ||
43 | #include "../net/if.h" | |
44 | #include "../net/netisr.h" | |
45 | #include "../net/route.h" | |
46 | ||
47 | #ifdef INET | |
48 | #include "../netinet/in.h" | |
49 | #include "../netinet/in_systm.h" | |
50 | #include "../netinet/in_var.h" | |
51 | #include "../netinet/ip.h" | |
52 | #include "../netinet/if_ether.h" | |
53 | #endif | |
54 | ||
55 | #ifdef NS | |
56 | #include "../netns/ns.h" | |
57 | #include "../netns/ns_if.h" | |
58 | #endif | |
59 | ||
385e8f24 KS |
60 | #ifdef ISO |
61 | #include "../netiso/iso.h" | |
62 | #include "../netiso/iso_var.h" | |
63 | #include "../netiso/iso_snpac.h" | |
64 | extern struct snpa_cache all_es, all_is; | |
65 | #endif | |
66 | ||
19df187f KS |
67 | #include "../tahoe/cpu.h" |
68 | #include "../tahoe/pte.h" | |
69 | #include "../tahoe/mtpr.h" | |
70 | ||
71 | #include "../tahoevba/vbavar.h" | |
72 | #include "if_exreg.h" | |
73 | #include "if_vba.h" | |
74 | ||
75 | ||
76 | #define NH2X 32 /* Host to eXcelan request buffers */ | |
77 | ||
78 | #define NX2H 16 /* eXcelan to Host reply buffers */ | |
79 | #define NREC 16 /* Number of RECeive buffers */ | |
80 | #define NTRB 4 /* Number of TRansmit Buffers */ | |
81 | #define NVBI (NREC + NTRB) | |
82 | ||
83 | #define EXWATCHINTVL 10 /* call exwatch every x secs */ | |
84 | ||
85 | int exprobe(), exslave(), exattach(), exintr(), exstart(); | |
86 | struct vba_device *exinfo[NEX]; | |
87 | ||
88 | long exstd[] = { 0 }; | |
89 | ||
90 | ||
91 | struct vba_driver exdriver = | |
92 | { exprobe, 0, exattach, exstart, exstd, "ex", exinfo }; | |
93 | int exinit(),ether_output(),exioctl(),exreset(),exwatch(); | |
94 | struct ex_msg *exgetcbuf(); | |
95 | int ex_ncall = 0; /* counts calls to exprobe */ | |
96 | u_long busoff; | |
97 | ||
98 | /* | |
99 | * Ethernet software status per interface. | |
100 | * | |
101 | * Each interface is referenced by a network interface structure, xs_if, which | |
102 | * the routing code uses to locate the interface. This structure contains the | |
103 | * output queue for the interface, its address, ... NOTE: To configure multiple | |
104 | * controllers, the sizeof this structure must be a multiple of 16 (xs_h2xhdr). | |
105 | */ | |
106 | struct ex_softc { | |
107 | struct arpcom xs_ac; /* Ethernet common part */ | |
108 | #define xs_if xs_ac.ac_if /* network-visible interface */ | |
109 | #define xs_addr xs_ac.ac_enaddr /* hardware Ethernet address */ | |
110 | int xs_flags; /* private flags */ | |
111 | #define EX_XPENDING 1 /* xmit rqst pending on EXOS */ | |
112 | #define EX_STATPENDING (1<<1) /* stats rqst pending on EXOS */ | |
113 | #define EX_RUNNING (1<<2) /* board is running */ | |
114 | #define EX_SETADDR (1<<3) /* physaddr has been changed */ | |
115 | int xs_cvec; /* probe stores cvec here */ | |
116 | short xs_enetunit; /* unit number for enet filtering */ | |
117 | short xs_enetinit; /* enet inetrface is initialized */ | |
118 | struct ex_msg *xs_h2xnext; /* host pointer to request queue */ | |
119 | struct ex_msg *xs_x2hnext; /* host pointer to reply queue */ | |
120 | u_long xs_qbaddr; /* map info for structs below */ | |
385e8f24 | 121 | struct ex_shm { |
19df187f | 122 | /* the following structures are always mapped in */ |
385e8f24 KS |
123 | u_short sm_h2xhdr; /* EXOS's request queue header */ |
124 | u_short sm_x2hhdr; /* EXOS's reply queue header */ | |
125 | struct ex_msg sm_h2xent[NH2X];/* request msg buffers */ | |
126 | struct ex_msg sm_x2hent[NX2H];/* reply msg buffers */ | |
127 | struct ex_conf sm_cm; /* configuration message */ | |
128 | struct ex_stat sm_xsa; /* EXOS writes stats here */ | |
19df187f | 129 | /* end mapped area */ |
385e8f24 KS |
130 | } *xs_shm; /* host pointer to shared area */ |
131 | #define xs_h2xhdr xs_shm->sm_h2xhdr | |
132 | #define xs_x2hhdr xs_shm->sm_x2hhdr | |
133 | #define xs_h2xent xs_shm->sm_h2xent | |
134 | #define xs_x2hent xs_shm->sm_x2hent | |
135 | #define xs_cm xs_shm->sm_cm | |
136 | #define xs_xsa xs_shm->sm_xsa | |
137 | #define BUSADDR(x) (0x3D000000 | (((u_long)kvtophys(x))&0xFFFFFF)) | |
138 | #define P_BUSADDR(x) (0x3D000000 | (((u_long)kvtophys(x))&0xFFFFF0)) | |
139 | #define INCORE_BASE(p) (((u_long)(p)->xs_shm) & 0xFFFFFFF0) | |
140 | /* we will arrange that the shared memory begins on a 16 byte boundary */ | |
141 | #define RVAL_OFF(n) (((char *)&(((struct ex_shm *)0)->n))-(char *)0) | |
142 | #define LVAL_OFF(n) (((char *)(((struct ex_shm *)0)->n))-(char *)0) | |
143 | #define H2XHDR_OFFSET RVAL_OFF(sm_h2xhdr) | |
144 | #define X2HHDR_OFFSET RVAL_OFF(sm_x2hhdr) | |
145 | #define H2XENT_OFFSET LVAL_OFF(sm_h2xent) | |
146 | #define X2HENT_OFFSET LVAL_OFF(sm_x2hent) | |
147 | #define CM_OFFSET RVAL_OFF(sm_cm) | |
148 | #define SA_OFFSET RVAL_OFF(sm_xsa) | |
19df187f KS |
149 | struct ifvba xs_vbinfo[NVBI];/* Bus Resources (low core) */ |
150 | struct ifvba *xs_pkblist; /* free list of above */ | |
385e8f24 KS |
151 | #define GetPkBuf(b, v) ((v = (b)->mb_pkb = xs->xs_pkblist),\ |
152 | (xs->xs_pkblist = (struct ifvba *)(v)->iff_mbuf)) | |
153 | #define FreePkBuf(v) (((v)->iff_mbuf = (struct mbuf *)xs->xs_pkblist),\ | |
154 | (xs->xs_pkblist = v)) | |
19df187f KS |
155 | char xs_nrec; /* number of pending receive buffers */ |
156 | char xs_ntrb; /* number of pending transmit buffers */ | |
19df187f KS |
157 | } ex_softc[NEX]; |
158 | ||
159 | int ex_padcheck = sizeof (struct ex_softc); | |
160 | ||
161 | exprobe(reg, vi) | |
162 | caddr_t reg; | |
163 | struct vba_device *vi; | |
164 | { | |
165 | register br, cvec; /* r12, r11 value-result */ | |
166 | register struct exdevice *exaddr = (struct exdevice *)reg; | |
167 | int i; | |
168 | ||
385e8f24 | 169 | if (badaddr((caddr_t)exaddr, 2)) |
19df187f KS |
170 | return 0; |
171 | /* | |
172 | * Reset EXOS and run self-test (should complete within 2 seconds). | |
173 | */ | |
174 | movow(&exaddr->ex_porta, EX_RESET); | |
175 | for (i = 1000000; i; i--) { | |
176 | uncache(&(exaddr->ex_portb)); | |
177 | if (exaddr->ex_portb & EX_TESTOK) | |
178 | break; | |
179 | } | |
180 | if ((exaddr->ex_portb & EX_TESTOK) == 0) | |
181 | return 0; | |
182 | br = 0x15; | |
183 | cvec = --vi->ui_hd->vh_lastiv; | |
184 | ex_softc[vi->ui_unit].xs_cvec = cvec; | |
185 | ex_ncall++; | |
186 | return (sizeof(struct exdevice)); | |
187 | } | |
188 | ||
189 | /* | |
190 | * Interface exists: make available by filling in network interface record. | |
191 | * System will initialize the interface when it is ready to accept packets. | |
192 | * A NET_ADDRS command is done to get the ethernet address. | |
193 | */ | |
194 | exattach(ui) | |
195 | register struct vba_device *ui; | |
196 | { | |
197 | register struct ex_softc *xs = &ex_softc[ui->ui_unit]; | |
198 | register struct ifnet *ifp = &xs->xs_if; | |
199 | register struct exdevice *exaddr = (struct exdevice *)ui->ui_addr; | |
200 | register struct ex_msg *bp; | |
201 | ||
202 | ifp->if_unit = ui->ui_unit; | |
203 | ifp->if_name = "ex"; | |
204 | ifp->if_mtu = ETHERMTU; | |
205 | ifp->if_init = exinit; | |
206 | ifp->if_ioctl = exioctl; | |
207 | ifp->if_output = ether_output; | |
208 | ifp->if_reset = exreset; | |
209 | ifp->if_start = exstart; | |
385e8f24 | 210 | ifp->if_flags = IFF_BROADCAST; |
19df187f | 211 | |
385e8f24 KS |
212 | /* |
213 | * Note: extra memory gets returned by if_vbareserve() | |
214 | * first, so, being page alligned, it is also 16-byte alligned. | |
215 | */ | |
216 | if (if_vbareserve(xs->xs_vbinfo, NVBI, EXMAXRBUF, | |
217 | (caddr_t *)&xs->xs_shm, sizeof(*xs->xs_shm)) == 0) | |
19df187f KS |
218 | return; |
219 | /* | |
220 | * Temporarily map queues in order to configure EXOS | |
221 | */ | |
222 | xs->xs_qbaddr = INCORE_BASE(xs); | |
223 | exconfig(ui, 0); /* without interrupts */ | |
224 | if (xs->xs_cm.cm_cc) | |
225 | return; /* bad conf */ | |
226 | /* | |
227 | * Get Ethernet address. | |
228 | */ | |
229 | if ((bp = exgetcbuf(xs, LLNET_ADDRS)) == (struct ex_msg *)0) | |
230 | panic("exattach"); | |
231 | bp->mb_na.na_mask = READ_OBJ; | |
232 | bp->mb_na.na_slot = PHYSSLOT; | |
233 | bp->mb_status |= MH_EXOS; | |
234 | movow(&exaddr->ex_portb, EX_NTRUPT); | |
235 | bp = xs->xs_x2hnext; | |
385e8f24 | 236 | while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */ |
19df187f KS |
237 | printf("ex%d: HW %c.%c NX %c.%c, hardware address %s\n", |
238 | ui->ui_unit, xs->xs_cm.cm_vc[2], xs->xs_cm.cm_vc[3], | |
239 | xs->xs_cm.cm_vc[0], xs->xs_cm.cm_vc[1], | |
240 | ether_sprintf(bp->mb_na.na_addrs)); | |
241 | bcopy((caddr_t)bp->mb_na.na_addrs, (caddr_t)xs->xs_addr, | |
242 | sizeof(xs->xs_addr)); | |
243 | if_attach(ifp); | |
244 | } | |
245 | ||
246 | /* | |
247 | * Reset of interface after BUS reset. | |
248 | * If interface is on specified vba, reset its state. | |
249 | */ | |
250 | exreset(unit) | |
251 | int unit; | |
252 | { | |
253 | register struct vba_device *ui; | |
254 | ||
255 | if (unit >= NEX || (ui = exinfo[unit]) == 0 || ui->ui_alive == 0) | |
256 | return; | |
257 | printf(" ex%d", unit); | |
258 | ex_softc[unit].xs_if.if_flags &= ~IFF_RUNNING; | |
259 | ex_softc[unit].xs_flags &= ~EX_RUNNING; | |
260 | ||
261 | exinit(unit); | |
262 | } | |
263 | ||
264 | /* | |
265 | * Initialization of interface; clear recorded pending operations, and | |
266 | * reinitialize BUS usage. Called at boot time, and at ifconfig time via | |
267 | * exioctl, with interrupts disabled. | |
268 | */ | |
269 | exinit(unit) | |
270 | int unit; | |
271 | { | |
272 | register struct ex_softc *xs = &ex_softc[unit]; | |
273 | register struct vba_device *ui = exinfo[unit]; | |
274 | register struct exdevice *exaddr = (struct exdevice *)ui->ui_addr; | |
275 | register struct ifnet *ifp = &xs->xs_if; | |
276 | register struct sockaddr_in *sin; | |
277 | register struct ex_msg *bp; | |
278 | int s; | |
279 | ||
280 | /* not yet, if address still unknown */ | |
281 | if (ifp->if_addrlist == (struct ifaddr *)0) | |
282 | return; | |
283 | if (xs->xs_flags & EX_RUNNING) | |
284 | return; | |
285 | ||
286 | xs->xs_qbaddr = INCORE_BASE(xs); | |
287 | exconfig(ui, 4); /* with vectored interrupts*/ | |
288 | ||
289 | /* | |
290 | * Put EXOS on the Ethernet, using NET_MODE command | |
291 | */ | |
292 | if ((bp = exgetcbuf(xs, LLNET_MODE)) == (struct ex_msg *)0) | |
293 | panic("exinit"); | |
294 | bp->mb_nm.nm_mask = WRITE_OBJ; | |
295 | bp->mb_nm.nm_optn = 0; | |
296 | bp->mb_nm.nm_mode = MODE_PERF; | |
297 | bp->mb_status |= MH_EXOS; | |
298 | movow(&exaddr->ex_portb, EX_NTRUPT); | |
299 | bp = xs->xs_x2hnext; | |
385e8f24 KS |
300 | while ((bp->mb_status & MH_OWNER) == MH_EXOS) /* poll for reply */ |
301 | ; | |
19df187f KS |
302 | bp->mb_length = MBDATALEN; |
303 | bp->mb_status |= MH_EXOS; /* free up buffer */ | |
304 | movow(&exaddr->ex_portb, EX_NTRUPT); | |
305 | xs->xs_x2hnext = xs->xs_x2hnext->mb_next; | |
306 | ||
307 | ifp->if_watchdog = exwatch; | |
308 | ifp->if_timer = EXWATCHINTVL; | |
309 | s = splimp(); /* are interrupts disabled here, anyway? */ | |
310 | exhangrcv(unit); | |
311 | xs->xs_if.if_flags |= IFF_RUNNING; | |
312 | xs->xs_flags |= EX_RUNNING; | |
313 | if (xs->xs_flags & EX_SETADDR) | |
314 | ex_setaddr((u_char *)0, unit); | |
385e8f24 KS |
315 | #ifdef ISO |
316 | ex_setmulti(all_es.sc_snpa, unit, 1); | |
317 | ex_setmulti(all_is.sc_snpa, unit, 2); | |
318 | #endif | |
19df187f KS |
319 | exstart(&ex_softc[unit].xs_if); /* start transmits */ |
320 | splx(s); /* are interrupts disabled here, anyway? */ | |
321 | } | |
322 | ||
323 | /* | |
324 | * Reset, test, and configure EXOS. It is called by exinit, and exattach. | |
325 | * Returns 0 if successful, 1 if self-test failed. | |
326 | */ | |
327 | exconfig(ui, itype) | |
328 | struct vba_device *ui; | |
329 | int itype; | |
330 | { | |
331 | register int unit = ui->ui_unit; | |
332 | register struct ex_softc *xs = &ex_softc[unit]; | |
333 | register struct exdevice *exaddr = (struct exdevice *) ui->ui_addr; | |
385e8f24 | 334 | register struct ex_conf *cm = &xs->xs_cm; |
19df187f KS |
335 | register struct ex_msg *bp; |
336 | register struct ifvba *pkb; | |
337 | int i; | |
338 | u_long shiftreg; | |
339 | static u_char cmaddr[8] = {0xFF, 0xFF, 0, 0}; | |
340 | ||
341 | xs->xs_flags = 0; | |
342 | /* | |
343 | * Reset EXOS, wait for self-test to complete | |
344 | */ | |
345 | movow(&exaddr->ex_porta, EX_RESET); | |
346 | do { | |
347 | uncache(&exaddr->ex_portb); | |
348 | } while ((exaddr->ex_portb & EX_TESTOK) == 0) ; | |
349 | /* | |
350 | * Set up configuration message. | |
351 | */ | |
352 | cm->cm_1rsrv = 1; | |
353 | cm->cm_cc = 0xFF; | |
354 | cm->cm_opmode = 0; /* link-level controller mode */ | |
355 | cm->cm_dfo = 0x0101; /* enable host data order conversion */ | |
356 | cm->cm_dcn1 = 1; | |
357 | cm->cm_2rsrv[0] = cm->cm_2rsrv[1] = 0; | |
358 | cm->cm_ham = 3; /* absolute address mode */ | |
359 | cm->cm_3rsrv = 0; | |
360 | cm->cm_mapsiz = 0; | |
361 | cm->cm_byteptrn[0] = 0x01; /* EXOS deduces data order of host */ | |
362 | cm->cm_byteptrn[1] = 0x03; /* by looking at this pattern */ | |
363 | cm->cm_byteptrn[2] = 0x07; | |
364 | cm->cm_byteptrn[3] = 0x0F; | |
365 | cm->cm_wordptrn[0] = 0x0103; | |
366 | cm->cm_wordptrn[1] = 0x070F; | |
367 | cm->cm_lwordptrn = 0x0103070F; | |
368 | for (i=0; i<20; i++) cm->cm_rsrvd[i] = 0; | |
369 | cm->cm_mba = 0xFFFFFFFF; | |
370 | cm->cm_nproc = 0xFF; | |
371 | cm->cm_nmbox = 0xFF; | |
372 | cm->cm_nmcast = 0xFF; | |
373 | cm->cm_nhost = 1; | |
374 | cm->cm_h2xba = P_BUSADDR(xs->xs_qbaddr); | |
375 | cm->cm_h2xhdr = H2XHDR_OFFSET; | |
376 | cm->cm_h2xtyp = 0; /* should never wait for rqst buffer */ | |
377 | cm->cm_x2hba = cm->cm_h2xba; | |
378 | cm->cm_x2hhdr = X2HHDR_OFFSET; | |
379 | cm->cm_x2htyp = itype; /* 0 for none, 4 for vectored */ | |
380 | cm->cm_x2haddr = xs->xs_cvec; /* ivec allocated in exprobe */ | |
381 | /* | |
382 | * Set up message queues and headers. | |
383 | * First the request queue | |
384 | */ | |
385 | for (bp = &xs->xs_h2xent[0]; bp < &xs->xs_h2xent[NH2X]; bp++) { | |
386 | bp->mb_link = (u_short)((char *)(bp+1)-INCORE_BASE(xs)); | |
387 | bp->mb_rsrv = 0; | |
388 | bp->mb_length = MBDATALEN; | |
389 | bp->mb_status = MH_HOST; | |
390 | bp->mb_next = bp+1; | |
391 | } | |
392 | xs->xs_h2xhdr = xs->xs_h2xent[NH2X-1].mb_link = (u_short)H2XENT_OFFSET; | |
393 | xs->xs_h2xnext = xs->xs_h2xent[NH2X-1].mb_next = xs->xs_h2xent; | |
394 | ||
395 | /* Now the reply queue. */ | |
396 | for (bp = &xs->xs_x2hent[0]; bp < &xs->xs_x2hent[NX2H]; bp++) { | |
397 | bp->mb_link = (u_short)((char *)(bp+1)-INCORE_BASE(xs)); | |
398 | bp->mb_rsrv = 0; | |
399 | bp->mb_length = MBDATALEN; | |
400 | bp->mb_status = MH_EXOS; | |
401 | bp->mb_next = bp+1; | |
402 | } | |
403 | xs->xs_x2hhdr = xs->xs_x2hent[NX2H-1].mb_link = (u_short)X2HENT_OFFSET; | |
404 | xs->xs_x2hnext = xs->xs_x2hent[NX2H-1].mb_next = xs->xs_x2hent; | |
405 | xs->xs_nrec = 0; | |
406 | xs->xs_ntrb = 0; | |
407 | xs->xs_pkblist = xs->xs_vbinfo + NVBI - 1; | |
385e8f24 | 408 | for (pkb = xs->xs_pkblist; pkb > xs->xs_vbinfo; pkb--) |
19df187f KS |
409 | pkb->iff_mbuf = (struct mbuf *)(pkb - 1); |
410 | xs->xs_vbinfo[0].iff_mbuf = 0; | |
411 | ||
412 | /* | |
413 | * Write config msg address to EXOS and wait for configuration to | |
414 | * complete (guaranteed response within 2 seconds). | |
415 | */ | |
416 | shiftreg = P_BUSADDR(xs->xs_qbaddr) + CM_OFFSET; | |
417 | for (i = 4; i < 8; i++) { | |
418 | cmaddr[i] = (u_char)(shiftreg & 0xFF); | |
419 | shiftreg >>= 8; | |
420 | } | |
421 | for (i = 0; i < 8; i++) { | |
422 | do { | |
423 | uncache(&exaddr->ex_portb); | |
424 | } while (exaddr->ex_portb & EX_UNREADY) ; | |
425 | DELAY(500); | |
426 | movow(&exaddr->ex_portb, cmaddr[i]); | |
427 | } | |
428 | for (i = 500000; i; --i) { | |
429 | DELAY(10); | |
430 | uncache(&cm->cm_cc); | |
431 | if (cm->cm_cc != 0xFF) | |
432 | break; | |
433 | } | |
434 | if (cm->cm_cc) | |
435 | printf("ex%d: configuration failed; cc=%x\n", unit, cm->cm_cc); | |
436 | } | |
437 | ||
438 | /* | |
439 | * Start or re-start output on interface. Get another datagram to send off of | |
440 | * the interface queue, and map it to the interface before starting the output. | |
441 | * This routine is called by exinit(), exoutput(), and excdint(). In all cases, | |
442 | * interrupts by EXOS are disabled. | |
443 | */ | |
444 | exstart(ifp) | |
445 | struct ifnet *ifp; | |
446 | { | |
447 | int unit = ifp->if_unit; | |
448 | struct vba_device *ui = exinfo[unit]; | |
449 | register struct ex_softc *xs = &ex_softc[unit]; | |
450 | struct exdevice *exaddr = (struct exdevice *)ui->ui_addr; | |
451 | register struct ex_msg *bp; | |
452 | register struct mbuf *m; | |
453 | int len; | |
454 | register struct ifvba *pkb; | |
385e8f24 KS |
455 | struct mbuf *m0 = 0; |
456 | register int nb = 0, tlen = 0; | |
19df187f KS |
457 | union l_util { |
458 | u_long l; | |
459 | struct i86_long i; | |
460 | } l_util; | |
461 | ||
462 | if (xs->xs_ntrb >= NTRB) | |
463 | return; | |
464 | if (xs->xs_pkblist == 0) { | |
465 | printf("ex%d: vbinfo exhausted, would panic", unit); | |
466 | return; | |
467 | } | |
468 | IF_DEQUEUE(&xs->xs_if.if_snd, m); | |
469 | if (m == 0) | |
470 | return; | |
471 | /* | |
472 | * Get a transmit request. | |
473 | */ | |
474 | if ((bp = exgetcbuf(xs, LLRTRANSMIT)) == (struct ex_msg *)0) { | |
475 | m_freem(m); | |
476 | printf("exstart: no command buffers\n"); | |
477 | return; | |
478 | } | |
479 | xs->xs_ntrb++; | |
385e8f24 | 480 | GetPkBuf(bp, pkb); |
19df187f KS |
481 | pkb->iff_mbuf = m; /* save mbuf pointer to free when done */ |
482 | /* | |
483 | * point directly to the first group of mbufs to be transmitted. The | |
484 | * hardware can only support NFRAGMENTS descriptors. | |
485 | */ | |
486 | while (m && ((nb < NFRAGMENTS-1) || (m->m_next == 0)) ) { | |
385e8f24 | 487 | l_util.l = BUSADDR(mtod(m, caddr_t)); |
19df187f KS |
488 | bp->mb_et.et_blks[nb].bb_len = (u_short)m->m_len; |
489 | bp->mb_et.et_blks[nb].bb_addr = l_util.i; | |
385e8f24 KS |
490 | if (l_util.l + m->m_len > BUSADDR(VB_MAXADDR24)) { |
491 | /* Here, the phys memory for the mbuf is out | |
492 | of range for the vmebus to talk to it */ | |
493 | if (m == pkb->iff_mbuf) | |
494 | pkb->iff_mbuf = 0; | |
495 | break; | |
496 | } | |
19df187f KS |
497 | tlen += m->m_len; |
498 | m0 = m; | |
499 | m = m->m_next; | |
500 | nb++; | |
501 | } | |
502 | ||
503 | /* 0 end of chain pointed to by iff_mbuf, to be freed when xmit done */ | |
504 | if (m0) | |
505 | m0->m_next = 0; | |
506 | ||
507 | /* | |
508 | * if not all of the descriptors would fit then merge remaining data | |
509 | * into the transmit buffer, and point to it. Note: the mbufs are freed | |
510 | * during the merge, they do not have to be freed when we get the | |
511 | * transmit interrupt. | |
512 | */ | |
513 | if (m) { | |
385e8f24 KS |
514 | if (m == pkb->iff_mbuf) { |
515 | printf("ex%d: exstart insanity\n", unit); | |
516 | pkb->iff_mbuf = 0; | |
517 | } | |
518 | len = if_vbaput(pkb->iff_buffer, m, 0); | |
19df187f KS |
519 | l_util.l = BUSADDR(pkb->iff_buffer); |
520 | bp->mb_et.et_blks[nb].bb_len = (u_short)len; | |
521 | bp->mb_et.et_blks[nb].bb_addr = l_util.i; | |
522 | tlen += len; | |
523 | nb++; | |
524 | } | |
525 | ||
526 | /* | |
385e8f24 KS |
527 | * If the total length of the packet is too small, |
528 | * pad the last fragment. (May run into very obscure problems) | |
19df187f | 529 | */ |
385e8f24 | 530 | if (tlen < sizeof(struct ether_header) + ETHERMIN) { |
19df187f KS |
531 | len = (ETHERMIN + sizeof(struct ether_header)) - tlen; |
532 | bp->mb_et.et_blks[nb-1].bb_len += (u_short)len; | |
533 | tlen += len; | |
385e8f24 KS |
534 | #ifdef notdef |
535 | if (l_util.l + m->m_len > BUSADDR(VB_MAXADDR24)) { | |
536 | must copy last frag into private buffer | |
537 | } | |
538 | #endif | |
19df187f KS |
539 | } |
540 | ||
541 | /* set number of fragments in descriptor */ | |
542 | bp->mb_et.et_nblock = nb; | |
543 | bp->mb_status |= MH_EXOS; | |
544 | movow(&exaddr->ex_portb, EX_NTRUPT); | |
545 | } | |
546 | ||
547 | /* | |
548 | * interrupt service routine. | |
549 | */ | |
550 | exintr(unit) | |
551 | int unit; | |
552 | { | |
553 | register struct ex_softc *xs = &ex_softc[unit]; | |
554 | register struct ex_msg *bp = xs->xs_x2hnext; | |
555 | struct vba_device *ui = exinfo[unit]; | |
556 | struct exdevice *exaddr = (struct exdevice *)ui->ui_addr; | |
385e8f24 | 557 | struct ex_msg *next_bp; |
19df187f | 558 | |
19df187f KS |
559 | while ((bp->mb_status & MH_OWNER) == MH_HOST) { |
560 | switch (bp->mb_rqst) { | |
561 | case LLRECEIVE: | |
385e8f24 KS |
562 | if (--xs->xs_nrec < 0) { |
563 | printf("ex%d: internal receive check\n", unit); | |
19df187f | 564 | xs->xs_nrec = 0; |
385e8f24 | 565 | } |
19df187f KS |
566 | exrecv(unit, bp); |
567 | FreePkBuf(bp->mb_pkb); | |
568 | bp->mb_pkb = (struct ifvba *)0; | |
569 | exhangrcv(unit); | |
570 | break; | |
571 | ||
572 | case LLTRANSMIT: | |
573 | case LLRTRANSMIT: | |
385e8f24 KS |
574 | if (--xs->xs_ntrb < 0) { |
575 | printf("ex%d: internal transmit check\n", unit); | |
19df187f | 576 | xs->xs_ntrb = 0; |
385e8f24 | 577 | } |
19df187f | 578 | xs->xs_if.if_opackets++; |
385e8f24 | 579 | if (bp->mb_rply == LL_OK || bp->mb_rply == LLXM_NSQE) |
19df187f KS |
580 | ; |
581 | else if (bp->mb_rply & LLXM_1RTRY) | |
582 | xs->xs_if.if_collisions++; | |
583 | else if (bp->mb_rply & LLXM_RTRYS) | |
584 | xs->xs_if.if_collisions += 2; /* guess */ | |
585 | else if (bp->mb_rply & LLXM_ERROR) | |
586 | if (xs->xs_if.if_oerrors++ % 100 == 0) | |
587 | printf("ex%d: 100 transmit errors=%b\n", | |
588 | unit, bp->mb_rply, XMIT_BITS); | |
589 | if (bp->mb_pkb->iff_mbuf) { | |
590 | m_freem(bp->mb_pkb->iff_mbuf); | |
591 | bp->mb_pkb->iff_mbuf = (struct mbuf *)0; | |
592 | } | |
593 | FreePkBuf(bp->mb_pkb); | |
594 | bp->mb_pkb = (struct ifvba *)0; | |
385e8f24 | 595 | exstart(&xs->xs_if); |
19df187f KS |
596 | exhangrcv(unit); |
597 | break; | |
598 | ||
599 | case LLNET_STSTCS: | |
600 | xs->xs_if.if_ierrors += xs->xs_xsa.sa_crc; | |
601 | xs->xs_flags &= ~EX_STATPENDING; | |
385e8f24 KS |
602 | case LLNET_ADDRS: |
603 | case LLNET_RECV: | |
604 | if (bp->mb_rply == LL_OK || bp->mb_rply == LLXM_NSQE) | |
605 | ; | |
606 | else | |
607 | printf("ex%d: %s, request 0x%x, reply 0x%x\n", | |
608 | unit, "unsucessful stat or address change", | |
609 | bp->mb_rqst, bp->mb_rply); | |
19df187f KS |
610 | break; |
611 | ||
612 | default: | |
613 | printf("ex%d: unknown reply 0x%x", unit, bp->mb_rqst); | |
614 | } | |
615 | bp->mb_length = MBDATALEN; | |
385e8f24 | 616 | next_bp = bp->mb_next; |
19df187f | 617 | bp->mb_status |= MH_EXOS; /* free up buffer */ |
385e8f24 | 618 | bp = next_bp; /* paranoia about race */ |
19df187f | 619 | movow(&exaddr->ex_portb, EX_NTRUPT); /* tell EXOS about it */ |
19df187f KS |
620 | } |
621 | xs->xs_x2hnext = bp; | |
622 | } | |
623 | ||
624 | /* | |
625 | * Get a request buffer, fill in standard values, advance pointer. | |
626 | */ | |
627 | struct ex_msg * | |
628 | exgetcbuf(xs, req) | |
629 | struct ex_softc *xs; | |
630 | int req; | |
631 | { | |
632 | register struct ex_msg *bp; | |
633 | struct ifvba *pkb; | |
634 | int s = splimp(); | |
635 | ||
636 | bp = xs->xs_h2xnext; | |
19df187f KS |
637 | if ((bp->mb_status & MH_OWNER) == MH_EXOS) { |
638 | splx(s); | |
639 | return (struct ex_msg *)0; | |
640 | } | |
641 | xs->xs_h2xnext = bp->mb_next; | |
642 | bp->mb_1rsrv = 0; | |
643 | bp->mb_rqst = req; | |
644 | bp->mb_length = MBDATALEN; | |
645 | bp->mb_pkb = (struct ifvba *)0; | |
646 | splx(s); | |
647 | return bp; | |
648 | } | |
649 | ||
650 | /* | |
651 | * Process Ethernet receive completion: If input error just drop packet, | |
652 | * otherwise examine packet to determine type. If can't determine length from | |
653 | * type, then have to drop packet, otherwise decapsulate packet based on type | |
654 | * and pass to type-specific higher-level input routine. | |
655 | */ | |
656 | exrecv(unit, bp) | |
657 | int unit; | |
658 | register struct ex_msg *bp; | |
659 | { | |
660 | register struct ex_softc *xs = &ex_softc[unit]; | |
661 | register struct ether_header *eh; | |
662 | register struct mbuf *m; | |
663 | int len, off, resid; | |
664 | register struct ifqueue *inq; | |
665 | int s; | |
666 | ||
667 | xs->xs_if.if_ipackets++; | |
668 | /* total length - header - crc */ | |
669 | len = bp->mb_er.er_blks[0].bb_len - sizeof(struct ether_header) - 4; | |
670 | if (bp->mb_rply != LL_OK) { | |
671 | if (xs->xs_if.if_ierrors++ % 100 == 0) | |
672 | printf("ex%d: 100 receive errors=%b\n", | |
673 | unit, bp->mb_rply, RECV_BITS); | |
674 | return; | |
675 | } | |
676 | eh = (struct ether_header *)(bp->mb_pkb->iff_buffer); | |
677 | ||
678 | /* | |
679 | * Deal with trailer protocol: if type is PUP trailer get true type from | |
680 | * first 16-bit word past data. Remember that type was trailer by | |
681 | * setting off. | |
682 | */ | |
683 | eh->ether_type = ntohs((u_short)eh->ether_type); | |
684 | #define exdataaddr(eh, off, type) ((type)(((caddr_t)((eh)+1)+(off)))) | |
685 | if (eh->ether_type >= ETHERTYPE_TRAIL && | |
686 | eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { | |
687 | off = (eh->ether_type - ETHERTYPE_TRAIL) * 512; | |
688 | if (off >= ETHERMTU) | |
689 | return; /* sanity */ | |
690 | eh->ether_type = ntohs(*exdataaddr(eh, off, u_short *)); | |
691 | resid = ntohs(*(exdataaddr(eh, off+2, u_short *))); | |
692 | if (off + resid > len) | |
693 | return; /* sanity */ | |
694 | len = off + resid; | |
695 | } else | |
696 | off = 0; | |
697 | if (len == 0) | |
698 | return; | |
699 | /* | |
700 | * Pull packet off interface. Off is nonzero if packet | |
701 | * has trailing header; if_vbaget will then force this header | |
702 | * information to be at the front, but we still have to drop | |
703 | * the type and length which are at the front of any trailer data. | |
704 | */ | |
705 | m = if_vbaget(bp->mb_pkb->iff_buffer, len, off, &xs->xs_if, 0); | |
706 | if (m == 0) | |
707 | return; | |
708 | ether_input(&xs->xs_if, eh, m); | |
709 | return; | |
710 | } | |
711 | ||
712 | /* | |
713 | * Hang a receive request. This routine is called by exinit and excdint, | |
714 | * with interrupts disabled in both cases. | |
715 | */ | |
716 | exhangrcv(unit) | |
717 | int unit; | |
718 | { | |
719 | register struct ex_softc *xs = &ex_softc[unit]; | |
720 | register struct ex_msg *bp; | |
721 | register struct ifvba *pkb; | |
722 | short mustint = 0; | |
723 | union l_util { | |
724 | u_long l; | |
725 | struct i86_long i; | |
726 | } l_util; | |
727 | ||
728 | while (xs->xs_nrec < NREC) { | |
729 | if (xs->xs_pkblist == (struct ifvba *)0) | |
730 | break; | |
731 | if ((bp = exgetcbuf(xs, LLRECEIVE)) == (struct ex_msg *)0) { | |
732 | break; | |
733 | } | |
385e8f24 KS |
734 | GetPkBuf(bp, pkb); |
735 | pkb->iff_mbuf = 0; | |
19df187f KS |
736 | xs->xs_nrec += 1; |
737 | bp->mb_er.er_nblock = 1; | |
738 | bp->mb_er.er_blks[0].bb_len = EXMAXRBUF; | |
739 | l_util.l = BUSADDR(pkb->iff_buffer); | |
740 | bp->mb_er.er_blks[0].bb_addr = l_util.i; | |
741 | bp->mb_status |= MH_EXOS; | |
742 | mustint = 1; | |
743 | } | |
744 | if (mustint == 0) | |
745 | return; | |
746 | movow(&((struct exdevice *)exinfo[unit]->ui_addr)->ex_portb, EX_NTRUPT); | |
747 | } | |
748 | ||
749 | /* | |
750 | * Ethernet output routine is ether_output(). | |
751 | */ | |
752 | ||
753 | /* | |
754 | * Watchdog routine (currently not used). Might use this to get stats from EXOS. | |
755 | */ | |
756 | exwatch(unit) | |
757 | int unit; | |
758 | { | |
759 | struct exdevice *exaddr = (struct exdevice *)exinfo[unit]->ui_addr; | |
760 | register struct ex_softc *xs = &ex_softc[unit]; | |
761 | register struct ex_msg *bp; | |
762 | int s = splimp(); | |
763 | ||
764 | if (xs->xs_flags & EX_STATPENDING) | |
765 | goto exspnd; | |
766 | if ((bp = exgetcbuf(xs, LLNET_STSTCS)) == (struct ex_msg *)0) { | |
767 | splx(s); | |
768 | return; | |
769 | } | |
770 | xs->xs_flags |= EX_STATPENDING; | |
771 | bp->mb_ns.ns_mask = READ_OBJ; | |
772 | bp->mb_ns.ns_rsrv = 0; | |
773 | bp->mb_ns.ns_nobj = 8; | |
774 | bp->mb_ns.ns_xobj = 0; | |
775 | bp->mb_ns.ns_bufp = P_BUSADDR(xs->xs_qbaddr) + SA_OFFSET; | |
776 | bp->mb_status |= MH_EXOS; | |
777 | movow(&exaddr->ex_portb, EX_NTRUPT); | |
778 | exspnd: splx(s); | |
779 | xs->xs_if.if_timer = EXWATCHINTVL; | |
780 | } | |
781 | ||
782 | /* | |
783 | * Process an ioctl request. | |
784 | */ | |
785 | exioctl(ifp, cmd, data) | |
786 | register struct ifnet *ifp; | |
787 | int cmd; | |
788 | caddr_t data; | |
789 | { | |
790 | register struct ifaddr *ifa = (struct ifaddr *)data; | |
791 | register struct ex_softc *xs = &ex_softc[ifp->if_unit]; | |
792 | int s = splimp(), error = 0; | |
793 | ||
794 | switch (cmd) { | |
795 | ||
796 | case SIOCSIFADDR: | |
797 | ifp->if_flags |= IFF_UP; | |
798 | exinit(ifp->if_unit); | |
799 | ||
800 | switch (ifa->ifa_addr->sa_family) { | |
801 | #ifdef INET | |
802 | case AF_INET: | |
803 | ((struct arpcom *)ifp)->ac_ipaddr = | |
804 | IA_SIN(ifa)->sin_addr; | |
805 | arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); | |
806 | break; | |
807 | #endif | |
808 | #ifdef NS | |
809 | case AF_NS: | |
810 | { | |
811 | register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); | |
812 | ||
813 | if (ns_nullhost(*ina)) | |
814 | ina->x_host = *(union ns_host *)(xs->xs_addr); | |
815 | else | |
816 | ex_setaddr(ina->x_host.c_host,ifp->if_unit); | |
817 | break; | |
818 | } | |
819 | #endif | |
820 | } | |
821 | break; | |
822 | ||
823 | case SIOCSIFFLAGS: | |
824 | if ((ifp->if_flags & IFF_UP) == 0 && | |
825 | xs->xs_flags & EX_RUNNING) { | |
826 | movow(&((struct exdevice *) | |
827 | (exinfo[ifp->if_unit]->ui_addr))->ex_porta, EX_RESET); | |
828 | xs->xs_flags &= ~EX_RUNNING; | |
829 | } else if (ifp->if_flags & IFF_UP && | |
830 | (xs->xs_flags & EX_RUNNING) == 0) | |
831 | exinit(ifp->if_unit); | |
832 | break; | |
833 | ||
834 | default: | |
835 | error = EINVAL; | |
836 | } | |
837 | splx(s); | |
838 | return (error); | |
839 | } | |
840 | ||
841 | /* | |
842 | * set ethernet address for unit | |
843 | */ | |
844 | ex_setaddr(physaddr, unit) | |
845 | u_char *physaddr; | |
846 | int unit; | |
847 | { | |
848 | register struct ex_softc *xs = &ex_softc[unit]; | |
19df187f KS |
849 | |
850 | if (physaddr) { | |
851 | xs->xs_flags |= EX_SETADDR; | |
852 | bcopy((caddr_t)physaddr, (caddr_t)xs->xs_addr, 6); | |
853 | } | |
385e8f24 KS |
854 | ex_setmulti((u_char *)xs->xs_addr, unit, PHYSSLOT); |
855 | } | |
856 | ||
857 | /* | |
858 | * Enable multicast reception for unit. | |
859 | */ | |
860 | ex_setmulti(linkaddr, unit, slot) | |
861 | u_char *linkaddr; | |
862 | int unit, slot; | |
863 | { | |
864 | register struct ex_softc *xs = &ex_softc[unit]; | |
865 | struct vba_device *ui = exinfo[unit]; | |
866 | register struct exdevice *addr= (struct exdevice *)ui->ui_addr; | |
867 | register struct ex_msg *bp; | |
868 | ||
869 | if (!(xs->xs_flags & EX_RUNNING)) | |
19df187f | 870 | return; |
385e8f24 | 871 | bp = exgetcbuf(xs, LLNET_ADDRS); |
19df187f | 872 | bp->mb_na.na_mask = READ_OBJ|WRITE_OBJ; |
385e8f24 KS |
873 | bp->mb_na.na_slot = slot; |
874 | bcopy((caddr_t)linkaddr, (caddr_t)bp->mb_na.na_addrs, 6); | |
19df187f KS |
875 | bp->mb_status |= MH_EXOS; |
876 | movow(&addr->ex_portb, EX_NTRUPT); | |
877 | bp = xs->xs_x2hnext; | |
385e8f24 | 878 | while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */ |
19df187f | 879 | #ifdef DEBUG |
385e8f24 KS |
880 | log(LOG_DEBUG, "ex%d: %s %s (slot %d)\n", unit, |
881 | (slot == PHYSSLOT ? "reset addr" : "add multicast" | |
882 | ether_sprintf(bp->mb_na.na_addrs), slot); | |
19df187f KS |
883 | #endif |
884 | /* | |
385e8f24 | 885 | * Now, re-enable reception on slot. |
19df187f | 886 | */ |
385e8f24 | 887 | bp = exgetcbuf(xs, LLNET_RECV); |
19df187f | 888 | bp->mb_nr.nr_mask = ENABLE_RCV|READ_OBJ|WRITE_OBJ; |
385e8f24 | 889 | bp->mb_nr.nr_slot = slot; |
19df187f KS |
890 | bp->mb_status |= MH_EXOS; |
891 | movow(&addr->ex_portb, EX_NTRUPT); | |
892 | bp = xs->xs_x2hnext; | |
385e8f24 | 893 | while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */ |
19df187f KS |
894 | ; |
895 | } | |
896 | #endif |