xref: /csrg-svn/sys/vax/if/if_uba.c (revision 5172)
1 /*	if_uba.c	4.4	81/12/03	*/
2 
3 #include "../h/param.h"
4 #include "../h/systm.h"
5 #include "../h/mbuf.h"
6 #include "../h/map.h"
7 #include "../h/pte.h"
8 #include "../h/buf.h"
9 #include "../h/ubareg.h"
10 #include "../h/ubavar.h"
11 #include "../h/cmap.h"
12 #include "../h/mtpr.h"
13 #include "../h/vmmac.h"
14 #include "../net/in.h"
15 #include "../net/in_systm.h"
16 #include "../net/if.h"
17 #include "../net/if_uba.h"
18 
19 /*
20  * Routines supporting UNIBUS network interfaces.
21  *
22  * TODO:
23  *	Support interfaces using only one BDP statically.
24  */
25 
26 /*
27  * Init UNIBUS for interface on uban whose headers of size hlen are to
28  * end on a page boundary.  We allocate a UNIBUS map register for the page
29  * with the header, and nmr more UNIBUS map registers for i/o on the adapter,
30  * doing this twice: once for reading and once for writing.  We also
31  * allocate page frames in the mbuffer pool for these pages.
32  */
33 if_ubainit(ifu, uban, hlen, nmr)
34 	register struct ifuba *ifu;
35 	int uban, hlen, nmr;
36 {
37 	register caddr_t cp = (caddr_t)m_pgalloc(2 * (nmr + 1));
38 	int i;
39 
40 COUNT(IF_UBAINIT);
41 	if (cp == 0)
42 		return (0);
43 	ifu->ifu_hlen = hlen;
44 	ifu->ifu_uban = uban;
45 	ifu->ifu_uba = uba_hd[uban].uh_uba;
46 	ifu->ifu_r.ifrw_addr = cp + NBPG - hlen;
47 	ifu->ifu_w.ifrw_addr = ifu->ifu_r.ifrw_addr + (nmr + 1) * NBPG;
48 	if (if_ubaalloc(ifu, &ifu->ifu_r, nmr) == 0)
49 		goto bad;
50 	if (if_ubaalloc(ifu, &ifu->ifu_w, nmr) == 0)
51 		goto bad2;
52 	for (i = 0; i < nmr; i++)
53 		ifu->ifu_wmap[i] = ifu->ifu_w.ifrw_mr[i+1];
54 	ifu->ifu_xswapd = 0;
55 	return (1);
56 bad2:
57 	ubarelse(ifu->ifu_uban, &ifu->ifu_r.ifrw_info);
58 bad:
59 	m_pgfree(cp, 2 * (nmr + 1));
60 	return (0);
61 }
62 
63 /*
64  * Setup either a ifrw structure by allocating UNIBUS map registers,
65  * a buffered data path, and initializing the fields of the ifrw structure
66  * to minimize run-time overhead.
67  */
68 static
69 if_ubaalloc(ifu, ifrw, nmr)
70 	struct ifuba *ifu;
71 	register struct ifrw *ifrw;
72 	int nmr;
73 {
74 	register int info;
75 
76 COUNT(IF_UBAALLOC);
77 	info =
78 	    uballoc(ifu->ifu_uban, ifrw->ifrw_addr, nmr*NBPG + ifu->ifu_hlen,
79 	        UBA_NEED16|UBA_NEEDBDP);
80 	if (info == 0)
81 		return (0);
82 	ifrw->ifrw_info = info;
83 	ifrw->ifrw_bdp = UBAI_BDP(info);
84 	ifrw->ifrw_proto = UBAMR_MRV | (UBAI_MR(info) << UBAMR_DPSHIFT);
85 	ifrw->ifrw_mr = &ifu->ifu_uba->uba_map[UBAI_MR(info) + 1];
86 	return (1);
87 }
88 
89 /*
90  * Pull read data off a interface.
91  * Len is length of data, with local net header stripped.
92  * Off is non-zero if a trailer protocol was used, and
93  * gives the offset of the trailer information.
94  * We copy the trailer information and then all the normal
95  * data into mbufs.  When full cluster sized units are present
96  * on the interface on cluster boundaries we can get them more
97  * easily by remapping, and take advantage of this here.
98  */
99 struct mbuf *
100 if_rubaget(ifu, totlen, off0)
101 	register struct ifuba *ifu;
102 	int totlen, off0;
103 {
104 	struct mbuf *top, **mp, *m;
105 	int off = off0, len;
106 	register caddr_t cp;
107 
108 COUNT(IF_RUBAGET);
109 
110 	top = 0;
111 	mp = &top;
112 	while (totlen > 0) {
113 		MGET(m, 0);
114 		if (m == 0)
115 			goto bad;
116 		if (off) {
117 			len = totlen - off;
118 			cp = ifu->ifu_r.ifrw_addr + ifu->ifu_hlen + off;
119 		} else
120 			len = totlen;
121 		if (len >= CLSIZE) {
122 			struct mbuf *p;
123 			struct pte *cpte, *ppte;
124 			int x, *ip, i;
125 
126 			MCLGET(p, 1);
127 			if (p == 0)
128 				goto nopage;
129 			m->m_len = CLSIZE;
130 			m->m_off = (int)p - (int)m;
131 			if (!claligned(cp))
132 				goto copy;
133 
134 			/*
135 			 * Switch pages mapped to UNIBUS with new page p,
136 			 * as quick form of copy.  Remap UNIBUS and invalidate.
137 			 */
138 			cpte = &Mbmap[mtocl(cp)*CLSIZE];
139 			ppte = &Mbmap[mtocl(p)*CLSIZE];
140 			x = btop(cp - ifu->ifu_r.ifrw_addr);
141 			ip = (int *)&ifu->ifu_r.ifrw_mr[x+1];
142 			for (i = 0; i < CLSIZE; i++) {
143 				struct pte t;
144 				t = *ppte; *ppte++ = *cpte; *cpte = t;
145 				*ip++ =
146 				    cpte++->pg_pfnum|ifu->ifu_r.ifrw_proto;
147 				mtpr(TBIS, cp);
148 				cp += NBPG;
149 				mtpr(TBIS, (caddr_t)p);
150 				p += NBPG / sizeof (*p);
151 			}
152 			goto nocopy;
153 		}
154 nopage:
155 		m->m_len = MIN(MLEN, len);
156 		m->m_off = MMINOFF;
157 copy:
158 		bcopy(cp, mtod(m, caddr_t), (unsigned)m->m_len);
159 		cp += m->m_len;
160 nocopy:
161 		*mp = m;
162 		mp = &m->m_next;
163 		if (off) {
164 			/* sort of an ALGOL-W style for statement... */
165 			off += m->m_len;
166 			if (off == totlen) {
167 				cp = ifu->ifu_r.ifrw_addr + ifu->ifu_hlen;
168 				off = 0;
169 				totlen -= off0;
170 			}
171 		}
172 	}
173 	return (top);
174 bad:
175 	m_freem(top);
176 	return (0);
177 }
178 
179 /*
180  * Map a chain of mbufs onto a network interface
181  * in preparation for an i/o operation.
182  * The argument chain of mbufs includes the local network
183  * header which is copied to be in the mapped, aligned
184  * i/o space.
185  */
186 if_wubaput(ifu, m)
187 	register struct ifuba *ifu;
188 	register struct mbuf *m;
189 {
190 	register struct mbuf *mp;
191 	register caddr_t cp, dp;
192 	register int i;
193 	int xswapd = 0;
194 	int x, cc;
195 
196 COUNT(IF_WUBAPUT);
197 	ifu->ifu_xswapd = 0;
198 	cp = ifu->ifu_w.ifrw_addr;
199 	while (m) {
200 		dp = mtod(m, char *);
201 		if (claligned(cp) && claligned(dp)) {
202 			struct pte *pte; int *ip;
203 			pte = &Mbmap[mtocl(dp)*CLSIZE];
204 			x = btop(cp - ifu->ifu_w.ifrw_addr);
205 			ip = (int *)&ifu->ifu_w.ifrw_mr[x + 1];
206 			for (i = 0; i < CLSIZE; i++)
207 				*ip++ =
208 				    ifu->ifu_w.ifrw_proto | pte++->pg_pfnum;
209 			ifu->ifu_xswapd |= 1 << (x>>(CLSHIFT-PGSHIFT));
210 			mp = m->m_next;
211 			m->m_next = ifu->ifu_xtofree;
212 			ifu->ifu_xtofree = m;
213 			cp += m->m_len;
214 		} else {
215 			bcopy(mtod(m, caddr_t), cp, (unsigned)m->m_len);
216 			cp += m->m_len;
217 			MFREE(m, mp);
218 		}
219 		m = mp;
220 	}
221 
222 	/*
223 	 * Xswapd is the set of clusters we just mapped out.  Ifu->ifu_xswapd
224 	 * is the set of clusters mapped out from before.  We compute
225 	 * the number of clusters involved in this operation in x.
226 	 * Clusters mapped out before and involved in this operation
227 	 * should be unmapped so original pages will be accessed by the device.
228 	 */
229 	cc = cp - ifu->ifu_w.ifrw_addr;
230 	x = ((cc - ifu->ifu_hlen) + CLBYTES - 1) >> CLSHIFT;
231 	xswapd &= ~ifu->ifu_xswapd;
232 	if (xswapd)
233 		while (i = ffs(xswapd)) {
234 			i--;
235 			if (i >= x)
236 				break;
237 			xswapd &= ~(1<<i);
238 			i *= CLSIZE;
239 			for (x = 0; x < CLSIZE; x++) {
240 				ifu->ifu_w.ifrw_mr[i] = ifu->ifu_wmap[i];
241 				i++;
242 			}
243 		}
244 	ifu->ifu_xswapd |= xswapd;
245 	return (cc);
246 }
247