Commit | Line | Data |
---|---|---|
49ff8a6a KS |
1 | /* |
2 | * Copyright (c) 1986 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_ix.c 6.1 (Berkeley) %G% | |
7 | */ | |
8 | ||
9 | #include "np.h" | |
10 | #if NNP > 0 | |
11 | ||
12 | /* | |
13 | * Interlan NP100 Ethernet Communications Controller interface | |
14 | */ | |
15 | #include "../machine/pte.h" | |
16 | ||
17 | #include "param.h" | |
18 | #include "systm.h" | |
19 | #include "mbuf.h" | |
20 | #include "buf.h" | |
21 | #include "protosw.h" | |
22 | #include "socket.h" | |
23 | #include "vmmac.h" | |
24 | #include "ioctl.h" | |
25 | #include "errno.h" | |
26 | ||
27 | #include "../net/if.h" | |
28 | #include "../net/netisr.h" | |
29 | #include "../net/route.h" | |
30 | ||
31 | #ifdef INET | |
32 | #include "../netinet/in.h" | |
33 | #include "../netinet/in_systm.h" | |
34 | #include "../netinet/in_var.h" | |
35 | #include "../netinet/ip.h" | |
36 | #include "../netinet/if_ether.h" | |
37 | #endif | |
38 | ||
39 | #ifdef NS | |
40 | #include "../netns/ns.h" | |
41 | #include "../netns/ns_if.h" | |
42 | #endif | |
43 | ||
44 | #include "../vax/cpu.h" | |
45 | #include "../vax/mtpr.h" | |
46 | #include "../vaxif/if_uba.h" | |
47 | #include "../vaxuba/ubareg.h" | |
48 | #include "../vaxuba/ubavar.h" | |
49 | #include "../vaxuba/npreg.h" | |
50 | #include "../vaxif/if_ix.h" | |
51 | ||
52 | int ixattach(), ixrint(), ixcint(); | |
53 | #define ILUNIT(x) minor(x) | |
54 | int ixinit(), ixoutput(), ixioctl(), ixreset(), ixwatch(); | |
55 | int (*IxAttach)() = ixattach; | |
56 | int (*IxReset)() = ixreset; | |
57 | ||
58 | /* | |
59 | * Ethernet software status per interface. | |
60 | * | |
61 | * Each interface is referenced by a network interface structure, | |
62 | * ix_if, which the routing code uses to locate the interface. | |
63 | * This structure contains the output queue for the interface, its address, ... | |
64 | * We also have, for each interface, a UBA interface structure, which | |
65 | * contains information about the UNIBUS resources held by the interface: | |
66 | * map registers, buffered data paths, etc. Information is cached in this | |
67 | * structure for use by the if_uba.c routines in running the interface | |
68 | * efficiently. | |
69 | */ | |
70 | struct ix_softc { | |
71 | struct arpcom ix_ac; /* Ethernet common part */ | |
72 | #define ix_if ix_ac.ac_if /* network-visible interface */ | |
73 | #define ix_addr ix_ac.ac_enaddr /* hardware Ethernet address */ | |
74 | int ix_flags; | |
75 | #define IXF_OACTIVE 0x1 /* output is active */ | |
76 | #define IXF_RCVPENDING 0x2 /* start rcv in ilcint */ | |
77 | #define IXF_GOTUBA 0x4 /* unibus resources mapped */ | |
78 | #define IXF_RUNNING 0x8 /* board is running */ | |
79 | #define IXF_SETADDR 0x10 /* physical address is changed */ | |
80 | #define IXF_STATPENDING 0x20 /* stat cmd pending */ | |
81 | #define IXF_GOTCQE 0x40 /* np resources available */ | |
82 | struct ifuba ix_ifuba; /* unibus resources */ | |
83 | u_short ix_aid; /* Access Id returned by open DDL */ | |
84 | u_short ix_badcqe; | |
85 | struct npmaster *ix_mp; /* Board physio request header */ | |
86 | struct npreq *ix_rrp; /* Cached npreq for recv */ | |
87 | struct npreq *ix_wrp; /* Cached npreq for xmit */ | |
88 | short ix_scaninterval; /* interval of stat collection */ | |
89 | #define IXWATCHINTERVAL 60 /* once every 60 seconds */ | |
90 | union ix_stats ix_stats; /* holds on-board statistics */ | |
91 | int ix_ubaddr; /* mapping registers of ix_stats */ | |
92 | } ix_softc[NNP]; | |
93 | extern struct uba_device *npdinfo[]; | |
94 | ||
95 | /* | |
96 | * Interface exists: make available by filling in network interface | |
97 | * record. System will initialize the interface when it is ready | |
98 | * to accept packets. We can't even get the ethernet address | |
99 | * or other interesting data until the board has been downloaded. | |
100 | * running ifconfig will attempt to start unit. | |
101 | */ | |
102 | ixattach(ui) | |
103 | struct uba_device *ui; | |
104 | { | |
105 | register struct ix_softc *ix = &ix_softc[ui->ui_unit]; | |
106 | register struct ifnet *ifp = &ix->ix_if; | |
107 | extern struct npmaster npmasters[]; | |
108 | ||
109 | ifp->if_unit = ui->ui_unit; | |
110 | ifp->if_name = "ix"; | |
111 | ifp->if_mtu = ETHERMTU; | |
112 | ifp->if_flags = IFF_BROADCAST; | |
113 | ||
114 | ifp->if_init = ixinit; | |
115 | ifp->if_output = ixoutput; | |
116 | ifp->if_ioctl = ixioctl; | |
117 | ifp->if_reset = ixreset; | |
118 | ||
119 | ix->ix_mp = npmasters + ui->ui_unit; | |
120 | ix->ix_ifuba.ifu_flags = UBA_CANTWAIT; | |
121 | ||
122 | if_attach(ifp); | |
123 | } | |
124 | ||
125 | struct npreq * | |
126 | ix_GetReq(mp, addr, len) | |
127 | struct npmaster *mp; | |
128 | caddr_t addr; | |
129 | { | |
130 | int unit = mp->unit; | |
131 | register struct npreq *rp; | |
132 | register struct CQE *ep; | |
133 | struct ix_softc *ix = ix_softc + unit; | |
134 | extern struct npreq *NpGetReq(); | |
135 | ||
136 | while ((rp = NpGetReq(mp->reqtab)) == NULL) { | |
137 | mp->reqtab->flags |= WANTREQ; | |
138 | sleep((caddr_t)(mp->reqtab), PZERO - 1); | |
139 | } | |
140 | rp->flags = KERNREQ; /* Clear flags */ | |
141 | ||
142 | ep = rp->element; /* Associated CQE */ | |
143 | ep->cqe_famid = (unsign32)ix; /* Process ID */ | |
144 | ep->cqe_wind = 0; /* Amount of buffer mapped */ | |
145 | ep->cqe_nbuf = 1; /* Must be 1, no buffer chain */ | |
146 | ep->cqe_char = 1; /* Driver owns this CQE */ | |
147 | ep->cqe_prot = NPDLA; /* Data Link Access protocol */ | |
148 | ep->cqe_bcnt = len; /* Byte count */ | |
149 | rp->bufaddr = (caddr_t) (UBADDRMASK & (int) addr);/* mapped buffer */ | |
150 | ep->cqe_dma[0] = (unsign16)LOWORD(rp->bufaddr); | |
151 | ep->cqe_dma[1] = (unsign16)HIWORD(rp->bufaddr); | |
152 | return (rp); | |
153 | } | |
154 | ||
155 | ix_DoReq(mp, rp, cmd, addr, len, rpb, routine) | |
156 | struct npmaster *mp; | |
157 | register struct npreq *rp; | |
158 | u_short cmd; | |
159 | caddr_t addr; | |
160 | int len; | |
161 | register u_short *rpb; | |
162 | int (*routine)(); | |
163 | { | |
164 | register struct CQE *ep = rp->element; | |
165 | register u_short *p = &ep->rpb1; | |
166 | u_short cnt = *rpb++; | |
167 | extern long NpDebug; | |
168 | int pri; | |
169 | ||
170 | ep->cqe_ust0 = ep->cqe_ust1 = NPCLEAR; /* Clear status */ | |
171 | ep->cqe_bcnt = len; /* Byte count */ | |
172 | rp->flags = KERNREQ; /* Clear flags */ | |
173 | rp->bufaddr = (caddr_t) (UBADDRMASK & (int) addr);/* mapped buffer */ | |
174 | rp->intr = routine; | |
175 | rp->user = (caddr_t) (ep->cqe_func = cmd);/* In case pissed on in CQE */ | |
176 | ep->cqe_dma[0] = (unsign16)LOWORD(rp->bufaddr); | |
177 | ep->cqe_dma[1] = (unsign16)HIWORD(rp->bufaddr); | |
178 | ep->cqe_lenrpb = cnt + cnt; | |
179 | for (; cnt > 0; cnt--) *p++ = *rpb++; | |
180 | ||
181 | if (NpDebug & DEBCQE) | |
182 | printf("Function is %x ep %x reqid %x\n", ep->cqe_func, ep, ep->cqe_reqid); | |
183 | if (NpDebug & DEBCQE) | |
184 | printf("irp len = %x rp = %x\n", ep->cqe_lenrpb, rp); | |
185 | if (routine == 0) { | |
186 | NpAddReq(mp->reqtab, rp); /* Queue onto active list */ | |
187 | while (!(rp->flags & REQDONE)) { | |
188 | pri = spl4(); | |
189 | NpAddCQE(ep, &mp->shmemp->devcq, mp); | |
190 | sleep((caddr_t)rp, PZERO - 1); | |
191 | splx(pri); | |
192 | } | |
193 | if (rp->flags & IOABORT || ep->cqe_sts != NPDONE | |
194 | || ep->cqe_ust0 != NPDONE | |
195 | || ep->cqe_ust1 != NPOK) { | |
196 | struct ix_softc *ix = (struct ix_softc *)ep->cqe_famid; | |
197 | printf("ix%d: Req failed, cmd %x, stat %x, ", | |
198 | ix->ix_if.if_unit, rp->user, ep->cqe_sts); | |
199 | printf("ust error %x,%x\n", ep->cqe_ust0, ep->cqe_ust1); | |
200 | } | |
201 | NpRemReq(rp); /* Clear request */ | |
202 | } else { | |
203 | pri = spl4(); | |
204 | NpAddCQE(ep, &mp->shmemp->devcq, mp); | |
205 | splx(pri); | |
206 | } | |
207 | } | |
208 | ||
209 | /* | |
210 | * Ethernet output routine. | |
211 | * Encapsulate a packet of type family for the local net. | |
212 | * Use trailer local net encapsulation if enough data in first | |
213 | * packet leaves a multiple of 512 bytes of data in remainder. | |
214 | */ | |
215 | ixoutput(ifp, m0, dst) | |
216 | struct ifnet *ifp; | |
217 | struct mbuf *m0; | |
218 | struct sockaddr *dst; | |
219 | { | |
220 | int type, s, error; | |
221 | u_char edst[6]; | |
222 | struct in_addr idst; | |
223 | register struct ix_softc *ix = &ix_softc[ifp->if_unit]; | |
224 | register struct mbuf *m = m0; | |
225 | register struct ether_header *il; | |
226 | register int off; | |
227 | int usetrailers; | |
228 | ||
229 | if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { | |
230 | error = ENETDOWN; | |
231 | goto bad; | |
232 | } | |
233 | switch (dst->sa_family) { | |
234 | ||
235 | #ifdef INET | |
236 | case AF_INET: | |
237 | idst = ((struct sockaddr_in *)dst)->sin_addr; | |
238 | if (!arpresolve(&ix->ix_ac, m, &idst, edst, &usetrailers)) | |
239 | return (0); /* if not yet resolved */ | |
240 | off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; | |
241 | if (usetrailers && off > 0 && (off & 0x1ff) == 0 && | |
242 | m->m_off >= MMINOFF + 2 * sizeof (u_short)) { | |
243 | type = ETHERTYPE_TRAIL + (off>>9); | |
244 | m->m_off -= 2 * sizeof (u_short); | |
245 | m->m_len += 2 * sizeof (u_short); | |
246 | *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP); | |
247 | *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len); | |
248 | goto gottrailertype; | |
249 | } | |
250 | type = ETHERTYPE_IP; | |
251 | off = 0; | |
252 | goto gottype; | |
253 | #endif | |
254 | #ifdef NS | |
255 | case AF_NS: | |
256 | type = ETHERTYPE_NS; | |
257 | bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host), | |
258 | (caddr_t)edst, sizeof (edst)); | |
259 | off = 0; | |
260 | goto gottype; | |
261 | #endif | |
262 | ||
263 | case AF_UNSPEC: | |
264 | il = (struct ether_header *)dst->sa_data; | |
265 | bcopy((caddr_t)il->ether_dhost, (caddr_t)edst, sizeof (edst)); | |
266 | type = il->ether_type; | |
267 | goto gottype; | |
268 | ||
269 | default: | |
270 | printf("ix%d: can't handle af%d\n", ifp->if_unit, | |
271 | dst->sa_family); | |
272 | error = EAFNOSUPPORT; | |
273 | goto bad; | |
274 | } | |
275 | ||
276 | gottrailertype: | |
277 | /* | |
278 | * Packet to be sent as trailer: move first packet | |
279 | * (control information) to end of chain. | |
280 | */ | |
281 | while (m->m_next) | |
282 | m = m->m_next; | |
283 | m->m_next = m0; | |
284 | m = m0->m_next; | |
285 | m0->m_next = 0; | |
286 | m0 = m; | |
287 | ||
288 | gottype: | |
289 | /* | |
290 | * Add local net header. If no space in first mbuf, | |
291 | * allocate another. | |
292 | */ | |
293 | if (m->m_off > MMAXOFF || | |
294 | MMINOFF + sizeof (struct ether_header) > m->m_off) { | |
295 | m = m_get(M_DONTWAIT, MT_HEADER); | |
296 | if (m == 0) { | |
297 | error = ENOBUFS; | |
298 | goto bad; | |
299 | } | |
300 | m->m_next = m0; | |
301 | m->m_off = MMINOFF; | |
302 | m->m_len = sizeof (struct ether_header); | |
303 | } else { | |
304 | m->m_off -= sizeof (struct ether_header); | |
305 | m->m_len += sizeof (struct ether_header); | |
306 | } | |
307 | il = mtod(m, struct ether_header *); | |
308 | il->ether_type = htons((u_short)type); | |
309 | bcopy((caddr_t)edst, (caddr_t)il->ether_dhost, sizeof (edst)); | |
310 | bcopy((caddr_t)ix->ix_addr, (caddr_t)il->ether_shost, | |
311 | sizeof(il->ether_shost)); | |
312 | ||
313 | /* | |
314 | * Queue message on interface, and start output if interface | |
315 | * not yet active. | |
316 | */ | |
317 | s = splimp(); | |
318 | if (IF_QFULL(&ifp->if_snd)) { | |
319 | IF_DROP(&ifp->if_snd); | |
320 | splx(s); | |
321 | m_freem(m); | |
322 | return (ENOBUFS); | |
323 | } | |
324 | IF_ENQUEUE(&ifp->if_snd, m); | |
325 | if ((ix->ix_flags & IXF_OACTIVE) == 0) | |
326 | ixstart(ifp->if_unit); | |
327 | splx(s); | |
328 | return (0); | |
329 | ||
330 | bad: | |
331 | m_freem(m0); | |
332 | return (error); | |
333 | } | |
334 | /* | |
335 | * Reset of interface after UNIBUS reset. | |
336 | * If interface is on specified uba, reset its state. | |
337 | */ | |
338 | ixreset(unit, uban, softp) | |
339 | int unit, uban; | |
340 | caddr_t softp; | |
341 | { | |
342 | register struct uba_device *ui; | |
343 | int mask = IXF_SETADDR; /* Only remember new physaddr */ | |
344 | ||
345 | if (unit >= NNP || (ui = npdinfo[unit]) == 0 || ui->ui_alive == 0 || | |
346 | ui->ui_ubanum != uban) | |
347 | return; | |
348 | printf(" ix%d reset", unit); | |
349 | if (softp) | |
350 | mask |= IXF_GOTUBA; /* UBA mapping regs still valid; */ | |
351 | ix_softc[unit].ix_if.if_flags &= ~IFF_RUNNING; | |
352 | ix_softc[unit].ix_flags &= mask; | |
353 | } | |
354 | ||
355 | ||
356 | /* | |
357 | * Initialization of interface; clear recorded pending | |
358 | * operations, and reinitialize UNIBUS usage. | |
359 | */ | |
360 | ixinit(unit) | |
361 | int unit; | |
362 | { | |
363 | register struct ix_softc *ix = &ix_softc[unit]; | |
364 | struct uba_device *ui = npdinfo[unit]; | |
365 | register struct ifnet *ifp = &ix->ix_if; | |
366 | register struct CQE *ep; | |
367 | struct npreq *rp; | |
368 | struct npmaster *mp = ix->ix_mp; | |
369 | register u_short *dpmp = & mp->shmemp->statblock.sb_dpm; | |
370 | u_short rpb[7]; | |
371 | int s; | |
372 | ||
373 | /* not yet, if address still unknown */ | |
374 | if (ifp->if_addrlist == (struct ifaddr *)0) | |
375 | return; | |
376 | if (ix->ix_flags & IXF_RUNNING) | |
377 | return; | |
378 | if ((mp->flags & AVAILABLE) == 0 || (*dpmp & PROTOMASK(NPDLA)) == 0) { | |
379 | ifp->if_flags &= ~IFF_UP; | |
380 | return; | |
381 | } | |
382 | if ((ix->ix_flags & IXF_GOTUBA) == 0) { | |
383 | ix->ix_ifuba.ifu_flags = UBA_CANTWAIT; | |
384 | if (if_ubainit(&ix->ix_ifuba, ui->ui_ubanum, | |
385 | sizeof (struct ether_header), (int)btoc(ETHERMTU)) == 0) { | |
386 | printf("ix%d: can't initialize\n", unit); | |
387 | ix->ix_if.if_flags &= ~IFF_UP; | |
388 | return; | |
389 | } | |
390 | ix->ix_ubaddr = uballoc(ui->ui_ubanum, (caddr_t)&ix->ix_stats, | |
391 | sizeof (union ix_stats), 0); | |
392 | ix->ix_flags |= IXF_GOTUBA; | |
393 | } | |
394 | if ((ix->ix_flags & IXF_GOTCQE) == 0) { | |
395 | ix->ix_rrp = ix_GetReq(mp, ix->ix_ifuba.ifu_r.ifrw_info, | |
396 | ETHERMTU); | |
397 | ix->ix_wrp = ix_GetReq(mp, 0, 0); | |
398 | ix->ix_flags |= IXF_GOTCQE; | |
399 | } | |
400 | ||
401 | rp = ix->ix_wrp; | |
402 | ep = rp->element; | |
403 | ||
404 | /* Changing the ethernet address resets the dla module, | |
405 | so must do it before opening the channel */ | |
406 | if (ix->ix_flags & IXF_SETADDR) { | |
407 | register char *cp = (char *) &ix->ix_stats; | |
408 | int spincount; | |
409 | int x; | |
410 | *cp++ = 1; | |
411 | bcopy(ix->ix_addr, (caddr_t)cp, 6); | |
412 | rpb[0] = 1; /* RPB length */ | |
413 | ix_DoReq(mp, rp, IXC_LDPA, ix->ix_ubaddr, 7, rpb, 0); | |
414 | #ifndef TheyFinallyFixedTheBoard | |
415 | /* Board requires some time to reinitialize its protocols */ | |
416 | x = spl1(); | |
417 | spincount = 2000000; | |
418 | while (((*dpmp & PROTOMASK(NPDLA))==0) && spincount > 0) | |
419 | spincount--; | |
420 | if (spincount==0) { | |
421 | printf("ix%d: failed to reinitialize DLA module\n", | |
422 | unit); | |
423 | splx(x); | |
424 | } | |
425 | splx(x); | |
426 | #endif | |
427 | } | |
428 | rpb[0] = 6; /* RPB length */ | |
429 | rpb[2] = 0x10; /* Share with any smart users */ | |
430 | rpb[3] = 0; /* Take (a copy of) all frames */ | |
431 | rpb[5] = 8; /* On board rcv queue length */ | |
432 | rpb[6] = 0; /* XMT packets as is */ | |
433 | ix_DoReq(mp, rp, IXC_OPEN, 0, 0, rpb, 0); | |
434 | ||
435 | ix->ix_aid = ep->rpb1; | |
436 | ||
437 | /* Here we request our ethernet address, if we didn't reset it*/ | |
438 | if ((ix->ix_flags & IXF_SETADDR)==0) { | |
439 | rpb[0] = 2; | |
440 | rpb[1] = ix->ix_aid; | |
441 | rpb[2] = 0; /* get all stats */ | |
442 | ix_DoReq(mp, rp, IXC_GSTAT, /* Get Stats */ | |
443 | (caddr_t) ix->ix_ubaddr, sizeof(ix->ix_stats) - 8, | |
444 | rpb, 0); | |
445 | bcopy((caddr_t) &ix->ix_stats, (caddr_t) ix->ix_addr, 6); | |
446 | } | |
447 | ix->ix_if.if_flags |= IFF_RUNNING; | |
448 | ix->ix_flags |= IXF_RUNNING; | |
449 | ifp->if_watchdog = ixwatch; | |
450 | ifp->if_timer = ix->ix_scaninterval = IXWATCHINTERVAL; | |
451 | ixrint(mp, 0); | |
452 | } | |
453 | ||
454 | /* | |
455 | * Start output on interface. | |
456 | * Get another datagram to send off of the interface queue, | |
457 | * and map it to the interface before starting the output. | |
458 | */ | |
459 | ixstart(dev) | |
460 | dev_t dev; | |
461 | { | |
462 | int len = 0; | |
463 | int unit = minor(dev); | |
464 | register struct ix_softc *ix = &ix_softc[unit]; | |
465 | register struct mbuf *n; | |
466 | struct mbuf *m; | |
467 | int s, error = 0; | |
468 | struct npmaster *mp = ix->ix_mp; | |
469 | struct npreq *rp = ix->ix_wrp; | |
470 | struct CQE *ep; | |
471 | u_short rpb[8]; | |
472 | ||
473 | IF_DEQUEUE(&ix->ix_if.if_snd, m); | |
474 | if (m == 0) { | |
475 | if (ix->ix_flags & IXF_STATPENDING) { | |
476 | ix->ix_flags |= IXF_OACTIVE; | |
477 | rpb[0] = 2; | |
478 | rpb[1] = ix->ix_aid; | |
479 | rpb[2] = 0; /* get all stats */ | |
480 | ix_DoReq(mp, rp, IXC_GSTAT, /* general Stats */ | |
481 | (caddr_t) ix->ix_ubaddr, sizeof(ix->ix_stats) - 8, | |
482 | rpb, ixcint); | |
483 | } | |
484 | return; | |
485 | } | |
486 | /* | |
487 | * Ensure minimum packet length. | |
488 | * This makes the safe assumtion that there are no virtual holes | |
489 | * after the data. | |
490 | * For security, it might be wise to zero out the added bytes, | |
491 | * but we're mainly interested in speed at the moment. | |
492 | */ | |
493 | len = if_wubaput(&ix->ix_ifuba, m); | |
494 | if (len - sizeof(struct ether_header) < ETHERMIN) | |
495 | len = ETHERMIN + sizeof(struct ether_header); | |
496 | ||
497 | ix->ix_flags |= IXF_OACTIVE; | |
498 | ||
499 | /* Now setup to call np driver */ | |
500 | rpb[0] = 8; | |
501 | rpb[1] = ix->ix_aid; | |
502 | ix_DoReq(mp, rp, IXC_XMIT, /* send frame */ | |
503 | ix->ix_ifuba.ifu_w.ifrw_info, len, rpb, ixcint); | |
504 | } | |
505 | ||
506 | /* | |
507 | * Command done interrupt. (almost) | |
508 | */ | |
509 | ixcint(mp, rp) | |
510 | struct npmaster *mp; | |
511 | struct npreq *rp; | |
512 | { | |
513 | struct CQE *ep; | |
514 | register struct ix_softc *ix; | |
515 | int s = splimp(); | |
516 | ||
517 | ep = rp->element; | |
518 | ix = (struct ix_softc *)ep->cqe_famid; | |
519 | if ((ix->ix_flags & IXF_OACTIVE) == 0) { | |
520 | printf("ix%d: stray xmit interrupt, npreq=%x\n", | |
521 | ix->ix_if.if_unit, rp); | |
522 | } | |
523 | ix->ix_flags &= ~IXF_OACTIVE; | |
524 | ||
525 | switch (ep->cqe_func) { | |
526 | ||
527 | case IXC_XMIT: | |
528 | if (ep->cqe_sts == 1) | |
529 | ix->ix_if.if_opackets++; | |
530 | else | |
531 | ix->ix_if.if_oerrors++; | |
532 | break; | |
533 | ||
534 | case IXC_GSTAT: | |
535 | if (ep->cqe_sts == 1) | |
536 | ix->ix_if.if_collisions = ix->ix_stats.ixg.macg_xrty; | |
537 | break; | |
538 | } | |
539 | if (ix->ix_ifuba.ifu_xtofree) { | |
540 | m_freem(ix->ix_ifuba.ifu_xtofree); | |
541 | ix->ix_ifuba.ifu_xtofree = 0; | |
542 | } | |
543 | done: | |
544 | ixstart(ix->ix_if.if_unit); | |
545 | splx(s); | |
546 | } | |
547 | ||
548 | /* | |
549 | * Ethernet interface receiver interrupt. | |
550 | * If input error just drop packet. | |
551 | * Otherwise purge input buffered data path and examine | |
552 | * packet to determine type. If can't determine length | |
553 | * from type, then have to drop packet. Othewise decapsulate | |
554 | * packet based on type and pass to type specific higher-level | |
555 | * input routine. | |
556 | */ | |
557 | ixrint(mp, rp) | |
558 | struct npmaster *mp; | |
559 | struct npreq *rp; | |
560 | { | |
561 | struct CQE *ep; | |
562 | register struct ix_softc *ix = ix_softc + mp->unit; | |
563 | register struct ether_header *il; | |
564 | struct mbuf *m; | |
565 | int len, off, resid, s; | |
566 | register struct ifqueue *inq; | |
567 | ||
568 | if ((ix->ix_flags & IXF_RUNNING) == 0) | |
569 | return; | |
570 | if (rp == 0) | |
571 | goto setup; | |
572 | ix->ix_flags &= ~IXF_RCVPENDING; | |
573 | ep = rp->element; | |
574 | ix->ix_if.if_ipackets++; | |
575 | if (ix->ix_ifuba.ifu_flags & UBA_NEEDBDP) | |
576 | UBAPURGE(ix->ix_ifuba.ifu_uba, ix->ix_ifuba.ifu_r.ifrw_bdp); | |
577 | il = (struct ether_header *)(ix->ix_ifuba.ifu_r.ifrw_addr); | |
578 | len = ep->cqe_bcnt - sizeof (struct ether_header); | |
579 | if (ep->cqe_sts != NPDONE | |
580 | || ep->cqe_ust0 != NPDONE | |
581 | || ep->cqe_ust1 != NPOK) { | |
582 | printf("ixrint: cqe error %x, %x, %x\n", | |
583 | ep->cqe_sts, ep->cqe_ust0, ep->cqe_ust1); | |
584 | if (++ix->ix_badcqe > 100) { | |
585 | ix->ix_badcqe = 0; | |
586 | printf("ixrint: shutting down unix dla\n"); | |
587 | ix->ix_if.if_flags &= ~IFF_UP; | |
588 | return; | |
589 | } | |
590 | goto setup; | |
591 | } | |
592 | ||
593 | if ( len < 46 || len > ETHERMTU) { | |
594 | ix->ix_if.if_ierrors++; | |
595 | #ifdef notdef | |
596 | if (ix->ix_if.if_ierrors % 100 == 0) | |
597 | printf("ix%d: += 100 input errors\n", unit); | |
598 | #endif | |
599 | goto setup; | |
600 | } | |
601 | ||
602 | /* | |
603 | * Deal with trailer protocol: if type is trailer type | |
604 | * get true type from first 16-bit word past data. | |
605 | * Remember that type was trailer by setting off. | |
606 | */ | |
607 | il->ether_type = ntohs((u_short)il->ether_type); | |
608 | #define ildataaddr(il, off, type) ((type)(((caddr_t)((il)+1)+(off)))) | |
609 | if (il->ether_type >= ETHERTYPE_TRAIL && | |
610 | il->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { | |
611 | off = (il->ether_type - ETHERTYPE_TRAIL) * 512; | |
612 | if (off >= ETHERMTU) | |
613 | goto setup; /* sanity */ | |
614 | il->ether_type = ntohs(*ildataaddr(il, off, u_short *)); | |
615 | resid = ntohs(*(ildataaddr(il, off+2, u_short *))); | |
616 | if (off + resid > len) | |
617 | goto setup; /* sanity */ | |
618 | len = off + resid; | |
619 | } else | |
620 | off = 0; | |
621 | if (len == 0) | |
622 | goto setup; | |
623 | ||
624 | /* | |
625 | * Pull packet off interface. Off is nonzero if packet | |
626 | * has trailing header; ilget will then force this header | |
627 | * information to be at the front, but we still have to drop | |
628 | * the type and length which are at the front of any trailer data. | |
629 | */ | |
630 | m = if_rubaget(&ix->ix_ifuba, len, off, &ix->ix_if); | |
631 | if (m == 0) | |
632 | goto setup; | |
633 | if (off) { | |
634 | struct ifnet *ifp; | |
635 | ||
636 | ifp = *(mtod(m, struct ifnet **)); | |
637 | m->m_off += 2 * sizeof (u_short); | |
638 | m->m_len -= 2 * sizeof (u_short); | |
639 | *(mtod(m, struct ifnet **)) = ifp; | |
640 | } | |
641 | switch (il->ether_type) { | |
642 | ||
643 | #ifdef INET | |
644 | case ETHERTYPE_IP: | |
645 | schednetisr(NETISR_IP); | |
646 | inq = &ipintrq; | |
647 | break; | |
648 | ||
649 | case ETHERTYPE_ARP: | |
650 | arpinput(&ix->ix_ac, m); | |
651 | goto setup; | |
652 | #endif | |
653 | #ifdef NS | |
654 | case ETHERTYPE_NS: | |
655 | schednetisr(NETISR_NS); | |
656 | inq = &nsintrq; | |
657 | break; | |
658 | ||
659 | #endif | |
660 | default: | |
661 | m_freem(m); | |
662 | goto setup; | |
663 | } | |
664 | ||
665 | s = splimp(); | |
666 | if (IF_QFULL(inq)) { | |
667 | IF_DROP(inq); | |
668 | m_freem(m); | |
669 | } else | |
670 | IF_ENQUEUE(inq, m); | |
671 | splx(s); | |
672 | ||
673 | setup: | |
674 | /* | |
675 | * Reset for next packet if possible. | |
676 | * If waiting for transmit command completion, set flag | |
677 | * and wait until command completes. | |
678 | */ | |
679 | if (rp == 0) { | |
680 | rp = ix->ix_rrp; | |
681 | rp->intr = ixrint; | |
682 | ep = rp->element; | |
683 | } | |
684 | len = ETHERMTU + sizeof(struct ether_header); | |
685 | ||
686 | /* Now setup to call np driver */ | |
687 | /* Initializations of request structure */ | |
688 | ||
689 | ep->cqe_func = IXC_RECV; /* get frame */ | |
690 | ep->cqe_ust0 = ep->cqe_ust1 = NPCLEAR; /* Clear status */ | |
691 | ep->cqe_bcnt = len; /* Byte count */ | |
692 | ep->cqe_lenrpb = 10; /* RPB length */ | |
693 | ep->rpb1 = ix->ix_aid; /* which channel */ | |
694 | ep->rpb2 = 65535; /* Timeout */ | |
695 | ||
696 | ix->ix_flags |= IXF_RCVPENDING; | |
697 | ||
698 | s = spl4(); | |
699 | NpAddCQE(ep, &mp->shmemp->devcq, mp); /* Add CQE to device's queue */ | |
700 | splx(s); | |
701 | } | |
702 | ||
703 | ||
704 | /* | |
705 | * Watchdog routine, request statistics from board. | |
706 | */ | |
707 | ixwatch(unit) | |
708 | int unit; | |
709 | { | |
710 | register struct ix_softc *ix = &ix_softc[unit]; | |
711 | register struct ifnet *ifp = &ix->ix_if; | |
712 | int s; | |
713 | ||
714 | if (ix->ix_flags & IXF_STATPENDING) { | |
715 | ifp->if_timer = ix->ix_scaninterval; | |
716 | return; | |
717 | } | |
718 | s = splimp(); | |
719 | ix->ix_flags |= IXF_STATPENDING; | |
720 | if ((ix->ix_flags & IXF_OACTIVE) == 0) | |
721 | ixstart(ifp->if_unit); | |
722 | splx(s); | |
723 | ifp->if_timer = ix->ix_scaninterval; | |
724 | } | |
725 | /* | |
726 | * Process an ioctl request. | |
727 | */ | |
728 | ixioctl(ifp, cmd, data) | |
729 | register struct ifnet *ifp; | |
730 | int cmd; | |
731 | caddr_t data; | |
732 | { | |
733 | register struct ifaddr *ifa = (struct ifaddr *)data; | |
734 | register struct ix_softc *ix = &ix_softc[ifp->if_unit]; | |
735 | int s = splimp(), error = 0; | |
736 | ||
737 | switch (cmd) { | |
738 | ||
739 | case SIOCSIFADDR: | |
740 | ifp->if_flags |= IFF_UP; | |
741 | ixinit(ifp->if_unit); | |
742 | if ((ifp->if_flags & IFF_UP) == 0) | |
743 | return (EBUSY); | |
744 | ||
745 | switch (ifa->ifa_addr.sa_family) { | |
746 | #ifdef INET | |
747 | case AF_INET: | |
748 | ((struct arpcom *)ifp)->ac_ipaddr = | |
749 | IA_SIN(ifa)->sin_addr; | |
750 | arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); | |
751 | break; | |
752 | #endif | |
753 | #ifdef NS | |
754 | case AF_NS: | |
755 | { | |
756 | register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); | |
757 | ||
758 | if (ns_nullhost(*ina)) { | |
759 | ina->x_host = * (union ns_host *) | |
760 | (ix_softc[ifp->if_unit].ix_addr); | |
761 | } else { | |
762 | return | |
763 | ix_setaddr(ina->x_host.c_host, ifp->if_unit); | |
764 | } | |
765 | break; | |
766 | } | |
767 | #endif | |
768 | } | |
769 | break; | |
770 | ||
771 | case SIOCSIFFLAGS: | |
772 | if ((ifp->if_flags & IFF_UP) == 0 && | |
773 | ix->ix_flags & IXF_RUNNING) { | |
774 | ix->ix_flags &= ~IXF_RUNNING; | |
775 | NpReset(ix->ix_mp, 0); | |
776 | } else if (ifp->if_flags & IFF_UP && | |
777 | (ix->ix_flags & IXF_RUNNING) == 0) | |
778 | ixinit(ifp->if_unit); | |
779 | break; | |
780 | ||
781 | default: | |
782 | error = EINVAL; | |
783 | } | |
784 | splx(s); | |
785 | return (error); | |
786 | } | |
787 | ||
788 | /* | |
789 | * set ethernet address for unit | |
790 | */ | |
791 | ix_setaddr(physaddr, unit) | |
792 | u_char *physaddr; | |
793 | int unit; | |
794 | { | |
795 | register struct ix_softc *ix = &ix_softc[unit]; | |
796 | ||
797 | if (! (ix->ix_flags & IXF_RUNNING)) | |
798 | return (EBUSY); | |
799 | ||
800 | /* The following is a big cop out due to the fact that | |
801 | Changing the ethernet address resets the dla module, | |
802 | so must re-open the channel, anyway. */ | |
803 | ||
804 | ||
805 | bcopy((caddr_t)physaddr, (caddr_t)ix->ix_addr, sizeof ix->ix_addr); | |
806 | ix->ix_flags &= ~IXF_RUNNING; | |
807 | ix->ix_flags |= IXF_SETADDR; | |
808 | ixinit(unit); | |
809 | NpKill(ix->ix_mp, ix->ix_rrp); | |
810 | } | |
811 | #endif |