remove __P() from this directory
[dragonfly.git] / sys / dev / atm / hfa / fore_receive.c
CommitLineData
984263bc
MD
1/*
2 *
3 * ===================================
4 * HARP | Host ATM Research Platform
5 * ===================================
6 *
7 *
8 * This Host ATM Research Platform ("HARP") file (the "Software") is
9 * made available by Network Computing Services, Inc. ("NetworkCS")
10 * "AS IS". NetworkCS does not provide maintenance, improvements or
11 * support of any kind.
12 *
13 * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
14 * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
15 * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
16 * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
17 * In no event shall NetworkCS be responsible for any damages, including
18 * but not limited to consequential damages, arising from or relating to
19 * any use of the Software or related support.
20 *
21 * Copyright 1994-1998 Network Computing Services, Inc.
22 *
23 * Copies of this Software may be made, however, the above copyright
24 * notice must be reproduced on all copies.
25 *
26 * @(#) $FreeBSD: src/sys/dev/hfa/fore_receive.c,v 1.5.2.2 2003/01/23 21:06:43 sam Exp $
38e94a25 27 * @(#) $DragonFly: src/sys/dev/atm/hfa/fore_receive.c,v 1.4 2003/08/27 10:35:16 rob Exp $
984263bc
MD
28 */
29
30/*
31 * FORE Systems 200-Series Adapter Support
32 * ---------------------------------------
33 *
34 * Receive queue management
35 *
36 */
37
1f2de5d4 38#include "fore_include.h"
984263bc 39
984263bc
MD
40/*
41 * Local functions
42 */
38e94a25 43static void fore_recv_stack (void *, KBuffer *);
984263bc
MD
44
45
46/*
47 * Allocate Receive Queue Data Structures
48 *
49 * Arguments:
50 * fup pointer to device unit structure
51 *
52 * Returns:
53 * 0 allocations successful
54 * else allocation failed
55 */
56int
57fore_recv_allocate(fup)
58 Fore_unit *fup;
59{
60 caddr_t memp;
61
62 /*
63 * Allocate non-cacheable memory for receive status words
64 */
65 memp = atm_dev_alloc(sizeof(Q_status) * RECV_QUELEN,
66 QSTAT_ALIGN, ATM_DEV_NONCACHE);
67 if (memp == NULL) {
68 return (1);
69 }
70 fup->fu_recv_stat = (Q_status *) memp;
71
72 memp = DMA_GET_ADDR(fup->fu_recv_stat, sizeof(Q_status) * RECV_QUELEN,
73 QSTAT_ALIGN, ATM_DEV_NONCACHE);
74 if (memp == NULL) {
75 return (1);
76 }
77 fup->fu_recv_statd = (Q_status *) memp;
78
79 /*
80 * Allocate memory for receive descriptors
81 */
82 memp = atm_dev_alloc(sizeof(Recv_descr) * RECV_QUELEN,
83 RECV_DESCR_ALIGN, 0);
84 if (memp == NULL) {
85 return (1);
86 }
87 fup->fu_recv_desc = (Recv_descr *) memp;
88
89 memp = DMA_GET_ADDR(fup->fu_recv_desc,
90 sizeof(Recv_descr) * RECV_QUELEN, RECV_DESCR_ALIGN, 0);
91 if (memp == NULL) {
92 return (1);
93 }
94 fup->fu_recv_descd = (Recv_descr *) memp;
95
96 return (0);
97}
98
99
100/*
101 * Receive Queue Initialization
102 *
103 * Allocate and initialize the host-resident receive queue structures
104 * and then initialize the CP-resident queue structures.
105 *
106 * Called at interrupt level.
107 *
108 * Arguments:
109 * fup pointer to device unit structure
110 *
111 * Returns:
112 * none
113 */
114void
115fore_recv_initialize(fup)
116 Fore_unit *fup;
117{
118 Aali *aap = fup->fu_aali;
119 Recv_queue *cqp;
120 H_recv_queue *hrp;
121 Recv_descr *rdp;
122 Recv_descr *rdp_dma;
123 Q_status *qsp;
124 Q_status *qsp_dma;
125 int i;
126
127 /*
128 * Point to CP-resident receive queue
129 */
130 cqp = (Recv_queue *)(fup->fu_ram + CP_READ(aap->aali_recv_q));
131
132 /*
133 * Point to host-resident receive queue structures
134 */
135 hrp = fup->fu_recv_q;
136 qsp = fup->fu_recv_stat;
137 qsp_dma = fup->fu_recv_statd;
138 rdp = fup->fu_recv_desc;
139 rdp_dma = fup->fu_recv_descd;
140
141 /*
142 * Loop thru all queue entries and do whatever needs doing
143 */
144 for (i = 0; i < RECV_QUELEN; i++) {
145
146 /*
147 * Set queue status word to free
148 */
149 *qsp = QSTAT_FREE;
150
151 /*
152 * Set up host queue entry and link into ring
153 */
154 hrp->hrq_cpelem = cqp;
155 hrp->hrq_status = qsp;
156 hrp->hrq_descr = rdp;
157 hrp->hrq_descr_dma = rdp_dma;
158 if (i == (RECV_QUELEN - 1))
159 hrp->hrq_next = fup->fu_recv_q;
160 else
161 hrp->hrq_next = hrp + 1;
162
163 /*
164 * Now let the CP into the game
165 */
166 cqp->cq_descr = (CP_dma) CP_WRITE(rdp_dma);
167 cqp->cq_status = (CP_dma) CP_WRITE(qsp_dma);
168
169 /*
170 * Bump all queue pointers
171 */
172 hrp++;
173 qsp++;
174 qsp_dma++;
175 rdp++;
176 rdp_dma++;
177 cqp++;
178 }
179
180 /*
181 * Initialize queue pointers
182 */
183 fup->fu_recv_head = fup->fu_recv_q;
184
185 return;
186}
187
188
189/*
190 * Drain Receive Queue
191 *
192 * This function will process all completed entries at the head of the
193 * receive queue. The received segments will be linked into a received
194 * PDU buffer chain and it will then be passed up the PDU's VCC stack for
195 * processing by the next higher protocol layer.
196 *
197 * May be called in interrupt state.
198 * Must be called with interrupts locked out.
199 *
200 * Arguments:
201 * fup pointer to device unit structure
202 *
203 * Returns:
204 * none
205 */
206void
207fore_recv_drain(fup)
208 Fore_unit *fup;
209{
210 H_recv_queue *hrp = NULL;
211 Recv_descr *rdp;
212 Recv_seg_descr *rsp;
213 Buf_handle *bhp;
214 Fore_vcc *fvp;
215 struct vccb *vcp;
216 KBuffer *m, *mhead, *mtail;
217 caddr_t cp;
218 u_long hdr, nsegs;
219 u_int seglen, type0;
220 int i, pdulen, retries = 0, error;
221
222 /* Silence the compiler */
223 mtail = NULL;
224 type0 = 0;
225
226 /*
227 * Process each completed entry
228 */
229retry:
230 while (*fup->fu_recv_head->hrq_status & QSTAT_COMPLETED) {
231
232 /*
233 * Get completed entry's receive descriptor
234 */
235 hrp = fup->fu_recv_head;
236 rdp = hrp->hrq_descr;
237
238#ifdef VAC
239 /*
240 * Cache flush receive descriptor
241 */
242 if (vac) {
243 vac_flush((addr_t)rdp, sizeof(Recv_descr));
244 }
245#endif
246
247 hdr = rdp->rd_cell_hdr;
248 nsegs = rdp->rd_nsegs;
249
250 pdulen = 0;
251 error = 0;
252 mhead = NULL;
253
254 /*
255 * Locate incoming VCC for this PDU
256 */
257 fvp = (Fore_vcc *) atm_dev_vcc_find((Cmn_unit *)fup,
258 ATM_HDR_GET_VPI(hdr), ATM_HDR_GET_VCI(hdr), VCC_IN);
259
260 /*
261 * Check for a receive error
262 *
263 * Apparently the receive descriptor itself contains valid
264 * information, but the received pdu data is probably bogus.
265 * We'll arrange for the receive buffer segments to be tossed.
266 */
267 if (*hrp->hrq_status & QSTAT_ERROR) {
268
269 fup->fu_pif.pif_ierrors++;
270 if (fvp) {
271 vcp = fvp->fv_connvc->cvc_vcc;
272 vcp->vc_ierrors++;
273 if (vcp->vc_nif)
274 vcp->vc_nif->nif_if.if_ierrors++;
275 }
276 ATM_DEBUG1("fore receive error: hdr=0x%lx\n", hdr);
277 error = 1;
278 }
279
280 /*
281 * Build PDU buffer chain from receive segments
282 */
283 for (i = 0, rsp = rdp->rd_seg; i < nsegs; i++, rsp++) {
284
285 bhp = rsp->rsd_handle;
286 seglen = rsp->rsd_len;
287
288 /*
289 * Remove buffer from our supplied queue and get
290 * to the underlying buffer
291 */
292 switch (bhp->bh_type) {
293
294 case BHT_S1_SMALL:
295 DEQUEUE(bhp, Buf_handle, bh_qelem,
296 fup->fu_buf1s_bq);
297 fup->fu_buf1s_cnt--;
298 m = (KBuffer *) ((caddr_t)bhp - BUF1_SM_HOFF);
299 KB_DATASTART(m, cp, caddr_t);
300 DMA_FREE_ADDR(cp, bhp->bh_dma, BUF1_SM_SIZE, 0);
301 break;
302
303 case BHT_S1_LARGE:
304 DEQUEUE(bhp, Buf_handle, bh_qelem,
305 fup->fu_buf1l_bq);
306 fup->fu_buf1l_cnt--;
307 m = (KBuffer *) ((caddr_t)bhp - BUF1_LG_HOFF);
308 KB_DATASTART(m, cp, caddr_t);
309 DMA_FREE_ADDR(cp, bhp->bh_dma, BUF1_LG_SIZE, 0);
310 break;
311
312 default:
313 log(LOG_ERR,
314 "fore_recv_drain: bhp=%p type=0x%x\n",
315 bhp, bhp->bh_type);
316 panic("fore_recv_drain: bad buffer type");
317 }
318
319 /*
320 * Toss any zero-length or receive error buffers
321 */
322 if ((seglen == 0) || error) {
323 KB_FREEALL(m);
324 continue;
325 }
326
327 /*
328 * Link buffer into chain
329 */
330 if (mhead == NULL) {
331 type0 = bhp->bh_type;
332 KB_LINKHEAD(m, mhead);
333 mhead = m;
334 } else {
335 KB_LINK(m, mtail);
336 }
337 KB_LEN(m) = seglen;
338 pdulen += seglen;
339 mtail = m;
340
341 /*
342 * Flush received buffer data
343 */
344#ifdef VAC
345 if (vac) {
346 addr_t dp;
347
348 KB_DATASTART(m, dp, addr_t);
349 vac_pageflush(dp);
350 }
351#endif
352 }
353
354 /*
355 * Make sure we've got a non-null PDU
356 */
357 if (mhead == NULL) {
358 goto free_ent;
359 }
360
361 /*
362 * We only support user data PDUs (for now)
363 */
364 if (hdr & ATM_HDR_SET_PT(ATM_PT_NONUSER)) {
365 KB_FREEALL(mhead);
366 goto free_ent;
367 }
368
369 /*
370 * Toss the data if there's no VCC
371 */
372 if (fvp == NULL) {
373 fup->fu_stats->st_drv.drv_rv_novcc++;
374 KB_FREEALL(mhead);
375 goto free_ent;
376 }
377
378#ifdef DIAGNOSTIC
379 if (atm_dev_print)
380 atm_dev_pdu_print((Cmn_unit *)fup, (Cmn_vcc *)fvp,
381 mhead, "fore_recv");
382#endif
383
384 /*
385 * Make sure we have our queueing headroom at the front
386 * of the buffer chain
387 */
388 if (type0 != BHT_S1_SMALL) {
389
390 /*
391 * Small buffers already have headroom built-in, but
392 * if CP had to use a large buffer for the first
393 * buffer, then we have to allocate a buffer here to
394 * contain the headroom.
395 */
396 fup->fu_stats->st_drv.drv_rv_nosbf++;
397
398 KB_ALLOCPKT(m, BUF1_SM_SIZE, KB_F_NOWAIT, KB_T_DATA);
399 if (m == NULL) {
400 fup->fu_stats->st_drv.drv_rv_nomb++;
401 KB_FREEALL(mhead);
402 goto free_ent;
403 }
404
405 /*
406 * Put new buffer at head of PDU chain
407 */
408 KB_LINKHEAD(m, mhead);
409 KB_LEN(m) = 0;
410 KB_HEADSET(m, BUF1_SM_DOFF);
411 mhead = m;
412 }
413
414 /*
415 * It looks like we've got a valid PDU - count it quick!!
416 */
417 mhead->m_pkthdr.rcvif = NULL;
418 mhead->m_pkthdr.csum_flags = 0;
419 SLIST_INIT(&mhead->m_pkthdr.tags);
420 KB_PLENSET(mhead, pdulen);
421 fup->fu_pif.pif_ipdus++;
422 fup->fu_pif.pif_ibytes += pdulen;
423 vcp = fvp->fv_connvc->cvc_vcc;
424 vcp->vc_ipdus++;
425 vcp->vc_ibytes += pdulen;
426 if (vcp->vc_nif) {
427 vcp->vc_nif->nif_ibytes += pdulen;
428 vcp->vc_nif->nif_if.if_ipackets++;
429#if (defined(BSD) && (BSD >= 199103))
430 vcp->vc_nif->nif_if.if_ibytes += pdulen;
431#endif
432 }
433
434 /*
435 * The STACK_CALL needs to happen at splnet() in order
436 * for the stack sequence processing to work. Schedule an
437 * interrupt queue callback at splnet() since we are
438 * currently at device level.
439 */
440
441 /*
442 * Prepend callback function pointer and token value to buffer.
443 * We have already guaranteed that the space is available
444 * in the first buffer.
445 */
446 KB_HEADADJ(mhead, sizeof(atm_intr_func_t) + sizeof(int));
447 KB_DATASTART(mhead, cp, caddr_t);
448 *((atm_intr_func_t *)cp) = fore_recv_stack;
449 cp += sizeof(atm_intr_func_t);
450 *((void **)cp) = (void *)fvp;
451
452 /*
453 * Schedule callback
454 */
455 if (!IF_QFULL(&atm_intrq)) {
456 IF_ENQUEUE(&atm_intrq, mhead);
457 SCHED_ATM;
458 } else {
459 fup->fu_stats->st_drv.drv_rv_ifull++;
460 KB_FREEALL(mhead);
461 goto free_ent;
462 }
463
464free_ent:
465 /*
466 * Mark this entry free for use and bump head pointer
467 * to the next entry in the queue
468 */
469 *hrp->hrq_status = QSTAT_FREE;
470 hrp->hrq_cpelem->cq_descr =
471 (CP_dma) CP_WRITE((u_long)hrp->hrq_descr_dma);
472 fup->fu_recv_head = hrp->hrq_next;
473 }
474
475 /*
476 * Nearly all of the interrupts generated by the CP will be due
477 * to PDU reception. However, we may receive an interrupt before
478 * the CP has completed the status word DMA to host memory. Thus,
479 * if we haven't processed any PDUs during this interrupt, we will
480 * wait a bit for completed work on the receive queue, rather than
481 * having to field an extra interrupt very soon.
482 */
483 if (hrp == NULL) {
484 if (++retries <= FORE_RECV_RETRY) {
485 DELAY(FORE_RECV_DELAY);
486 goto retry;
487 }
488 }
489
490 return;
491}
492
493
494/*
495 * Pass Incoming PDU up Stack
496 *
497 * This function is called via the core ATM interrupt queue callback
498 * set in fore_recv_drain(). It will pass the supplied incoming
499 * PDU up the incoming VCC's stack.
500 *
501 * Called at splnet.
502 *
503 * Arguments:
504 * tok token to identify stack instantiation
505 * m pointer to incoming PDU buffer chain
506 *
507 * Returns:
508 * none
509 */
510static void
511fore_recv_stack(tok, m)
512 void *tok;
513 KBuffer *m;
514{
515 Fore_vcc *fvp = (Fore_vcc *)tok;
516 int err;
517
518 /*
519 * Send the data up the stack
520 */
521 STACK_CALL(CPCS_UNITDATA_SIG, fvp->fv_upper,
522 fvp->fv_toku, fvp->fv_connvc, (int)m, 0, err);
523 if (err)
524 KB_FREEALL(m);
525
526 return;
527}
528
529
530/*
531 * Free Receive Queue Data Structures
532 *
533 * Arguments:
534 * fup pointer to device unit structure
535 *
536 * Returns:
537 * none
538 */
539void
540fore_recv_free(fup)
541 Fore_unit *fup;
542{
543 /*
544 * We'll just let fore_buf_free() take care of freeing any
545 * buffers sitting on the receive queue (which are also still
546 * on the fu_*_bq queue).
547 */
548 if (fup->fu_flags & CUF_INITED) {
549 }
550
551 /*
552 * Free the status words
553 */
554 if (fup->fu_recv_stat) {
555 if (fup->fu_recv_statd) {
556 DMA_FREE_ADDR(fup->fu_recv_stat, fup->fu_recv_statd,
557 sizeof(Q_status) * RECV_QUELEN,
558 ATM_DEV_NONCACHE);
559 }
560 atm_dev_free((volatile void *)fup->fu_recv_stat);
561 fup->fu_recv_stat = NULL;
562 fup->fu_recv_statd = NULL;
563 }
564
565 /*
566 * Free the receive descriptors
567 */
568 if (fup->fu_recv_desc) {
569 if (fup->fu_recv_descd) {
570 DMA_FREE_ADDR(fup->fu_recv_desc, fup->fu_recv_descd,
571 sizeof(Recv_descr) * RECV_QUELEN, 0);
572 }
573 atm_dev_free(fup->fu_recv_desc);
574 fup->fu_recv_desc = NULL;
575 fup->fu_recv_descd = NULL;
576 }
577
578 return;
579}
580