remove __P() from this directory
[dragonfly.git] / sys / dev / atm / hea / eni_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/hea/eni_receive.c,v 1.5 1999/08/28 00:41:45 peter Exp $
38e94a25 27 * @(#) $DragonFly: src/sys/dev/atm/hea/eni_receive.c,v 1.5 2003/08/27 10:35:15 rob Exp $
984263bc
MD
28 */
29
30/*
31 * Efficient ENI Adapter Support
32 * -----------------------------
33 *
34 * Receive management
35 *
36 */
37
d2438d69 38#include <netproto/atm/kern_include.h>
984263bc 39
1f2de5d4
MD
40#include "eni_stats.h"
41#include "eni.h"
42#include "eni_var.h"
984263bc 43
38e94a25 44static void eni_recv_stack (void *, KBuffer *);
984263bc
MD
45
46#ifdef DIAGNOSTIC
47extern int eni_pdu_print;
48#endif
49
50/*
51 * Procedure to remove VCs from the Service List and generate DMA
52 * requests to move the associated PDUs into host memory. As PDUs
53 * are completed in adapter memory, the adapter examines the IN_SERVICE
54 * bit for the VC in the VC table. If this bit is not set, the adapter
55 * will place the VC number at the end of the service list queue, set
56 * the IN_SERVICE bit in the VC table, and interrupt the host. The host
57 * will remove VCs from the service list, clear the IN_SERVICE bit in
58 * the VC table, and create a DMA list to move the PDU into host buffers.
59 *
60 * Arguments:
61 * eup pointer to per unit structure
62 *
63 * Returns:
64 * none
65 *
66 */
67void
68eni_do_service ( eup )
69 Eni_unit *eup;
70{
71 int vcc;
72 Eni_vcc *evp;
73 u_long servwrite;
74 VCI_Table *vct;
75 u_long rdptr;
76 u_long *rxp;
77 KBuffer *m;
78 u_long dma[TEMP_DMA_SIZE];
79 u_long i, j;
80 u_long dma_rd, dma_wr;
81 u_long dma_avail;
82 int pdulen;
83 int mask;
84 u_long *upp;
85
86 /*
87 * Where is the adapter currently inserting entries?
88 */
89 servwrite = eup->eu_midway[MIDWAY_SVCWR] & SVC_SIZE_MASK;
90 /*
91 * As long as we're not caught up with the adapter, keep
92 * removing VCs from the service list.
93 */
94 while ( servwrite != eup->eu_servread ) {
95 int vci_hdr;
96 u_long descr;
97
98 /*
99 * Get VC number and find VC table entry.
100 */
101 vcc = eup->eu_svclist[eup->eu_servread];
102 vct = &eup->eu_vcitbl[vcc];
103 vci_hdr = vct->vci_control; /* Current status */
104
105 /*
106 * Check that this VCC still needs servicing. We
107 * might have closed this VCC down in between
108 * the adapter setting the flag and our checking
109 * the flag. Also check that we haven't placed the
110 * VCC into TRASH mode.
111 */
112 if ( ( vci_hdr & VCI_IN_SERVICE ) == 0 ||
113 ( (vci_hdr & ~VCI_MODE_MASK) ==
114 (VCI_MODE_TRASH << VCI_MODE_SHIFT) ) )
115 goto next_vcc;
116
117 /*
118 * Find the size of this VCs buffer
119 */
120 mask = (vci_hdr >> VCI_SIZE_SHIFT) & VCI_SIZE_MASK;
121 mask = 1 << (ENI_LOC_PREDIV + mask);
122 /* Turn byte count into word count */
123 mask >>= 2;
124 /*
125 * Find the start of the adapter buffer for this VC.
126 */
127 rxp = (u_long *)
128 ((int)(((vci_hdr >> VCI_LOC_SHIFT ) & VCI_LOC_MASK)
129 << ENI_LOC_PREDIV) + (int)eup->eu_ram);
130 /*
131 * Locate incoming VCC for this PDU and find where we
132 * should next read from.
133 */
134 evp = (Eni_vcc *) atm_dev_vcc_find ( (Cmn_unit *)eup,
135 0, vcc, VCC_IN );
136 if ( evp == (Eni_vcc *)NULL )
137 goto next_vcc; /* VCI no longer active */
138 rdptr = evp->ev_rxpos;
139 /*
140 * Find out where the adapter is currently reassembling.
141 * The PDU which starts at descr is not yet complete so we
142 * must stop there.
143 */
144 descr = ( vct->vci_descr >> 16 ) & 0x7FFF;
145 /*
146 * As long as we haven't processed all the completed PDUs on
147 * this VC, keep going...
148 */
149 while ( rdptr != descr )
150 {
151 int n_cells;
152 int pdu_descr;
153 int aal5;
154
155 /*
156 * Ensure that the following are reset for every new
157 * PDU.
158 */
159 upp = NULL;
160 m = NULL;
161
162 /*
163 * Fisrt build a DMA with JK to skip the descriptor word.
164 * We must always skip the descriptor even if it turns out
165 * that there isn't any PDU here.
166 */
167 j = 0;
168 dma[j++] = (((rdptr + 1) & (mask-1)) << DMA_COUNT_SHIFT ) |
169 ( vcc << DMA_VCC_SHIFT ) | DMA_JK;
170 dma[j++] = 0;
171
172 /*
173 * We'll use some of the values below for skipping
174 * bad PDUs or counting statistics so compute them
175 * now.
176 */
177
178 /*
179 * Grab a copy of the descriptor word
180 */
181 pdu_descr = rxp[rdptr];
182
183 /*
184 * Strip out cell count from descriptor word.
185 * At this point, we still don't know if there
186 * is any real data until after we check for
187 * TRASH mode.
188 */
189 n_cells = pdu_descr & DESCR_CELL_COUNT;
190
191 /*
192 * Is this an AAL5 PDU? Check MODE in vci_hdr.
193 */
194 aal5 = ( ( vci_hdr & ~VCI_MODE_MASK ) ==
195 VCI_MODE_AAL5 << VCI_MODE_SHIFT );
196
197 /*
198 * Now check to see if we're trashing on this vcc.
199 * If so, there is no data with this VC and the
200 * next word after the current descriptor is the
201 * descriptor for the next PDU.
202 */
203 if ( ( pdu_descr & DESCR_TRASH_BIT ) != 0 ) {
204 if ( aal5 )
205 /*
206 * Count as number of AAL5 cells dropped
207 */
208 eup->eu_stats.eni_st_aal5.aal5_drops += n_cells;
209 else
210 /*
211 * Count as number of AAL0 cells dropped
212 */
213 eup->eu_stats.eni_st_aal0.aal0_drops += n_cells;
214 eup->eu_pif.pif_ierrors++;
215 /*
216 * When cells have been trashed, all we have in the
217 * buffer is a descriptor word. There are no data
218 * words. Set the number of cells to zero so that
219 * we correctly skip to the next word which will
220 * be the descriptor for the next PDU.
221 */
222 n_cells = 0;
223 /*
224 * Go issue the DMA to skip this descriptor word.
225 */
226 goto send_dma;
227 }
228
229 /*
230 * Data length: number of cells * cell size
231 */
232 pdulen = n_cells * BYTES_PER_CELL;
233
234 /*
235 * If this is an AAL5 PDU, then we need to check
236 * for the presence of any CRC errors. If there
237 * is one or more CRC errors, then we are going to
238 * drop this PDU.
239 */
240 if ( aal5 && ( pdu_descr & DESCR_CRC_ERR ) ) {
241 /*
242 * Count the stat
243 */
244 eup->eu_pif.pif_ierrors++;
245 eup->eu_stats.eni_st_aal5.aal5_pdu_crc++;
246 if ( evp->ev_connvc->cvc_vcc )
247 evp->ev_connvc->cvc_vcc->vc_ierrors++;
248 /*
249 * Build a DMA entry to skip the rest of this
250 * PDU.
251 */
252 dma[j++] =
253 (((rdptr + n_cells*WORDS_PER_CELL + 1)
254 & (mask-1)) << DMA_COUNT_SHIFT ) |
255 (vcc << DMA_VCC_SHIFT ) | DMA_JK;
256 dma[j++] = 0;
257 /*
258 * All done with this PDU. Get a buffer to save some
259 * data for reclamation services.
260 */
261 KB_ALLOCPKT ( m, ENI_SMALL_BSIZE, KB_F_NOWAIT,
262 KB_T_DATA );
263 if ( m ) {
264 u_long *up;
265
266 KB_DATASTART ( m, up, u_long * );
267 /*
268 * Indicate no PDU
269 */
270 KB_PLENSET ( m, 0 );
271 /*
272 * Set buffer length - only driver overhead
273 */
274 KB_LEN ( m ) = 3 * sizeof ( u_long );
275 /*
276 * Insert vcc, space for DMA pointers,
277 * and pdulen
278 */
279 *up++ = vcc;
280 upp = up; /* Remember location */
281 up++; /* And skip it */
282 /* - to be filled later */
283 *up = pdulen; /* Actual PDU length if it */
284 /* were valid */
285 } else {
286 /*
287 * We've a real problem here as now we can't
288 * reclaim/advance resources/safety pointers.
289 */
290 eup->eu_stats.eni_st_drv.drv_rv_norsc++;
291#ifdef DO_LOG
292 log ( LOG_ERR,
293 "eni_do_service: No drain buffers available. Receiver about to lock.\n" );
294#endif
295 }
296 goto send_dma;
297 }
298
299 /*
300 * Do we need to strip the AAL layer? Yes if this
301 * is an AAL5 PDU.
302 */
303 if ( aal5 ) {
304 /*
305 * Grab the CS-PDU length. Find the address of the
306 * last word, back up one word to skip CRC, and
307 * then mask the whole thing to handle circular wraps.
308 */
309 pdulen = rxp[(rdptr + n_cells*WORDS_PER_CELL - 1)
310 & (mask-1)]
311 & 0xFFFF;
312 }
313
314 /*
315 * We now have a valid PDU of some length. Build
316 * the necessary DMA list to move it into host
317 * memory.
318 */
319
320 /*
321 * Get an initial buffer.
322 */
323 KB_ALLOCPKT ( m, ENI_SMALL_BSIZE, KB_F_NOWAIT, KB_T_DATA );
324 /*
325 * Do we have a valid buffer?
326 */
327 if ( m != (KBuffer *)NULL )
328 {
329 int len;
330 u_long *up;
331 KBuffer *m0;
332
333 KB_DATASTART ( m, up, u_long * );
334 /*
335 * Fill in pdulen in PKTHDR structure (for IP).
336 */
337 KB_PLENSET ( m, pdulen );
338 /*
339 * We're going to save the VCI nuber, the start
340 * and stop DMA pointers, and the PDU length at
341 * the head of the buffer. We'll pull this out
342 * later after the DMA has completed.
343 *
344 * Insert VCI number as first word in first buffer,
345 * remeber where we want to store the start/stop
346 * pointers, and store the PDU length.
347 */
348 *up++ = vcc; /* PDU's VCC */
349 upp = up; /* Remember where we are */
350 up++; /* To stuff start/stop pointers in */
351 *up++ = pdulen; /* PDU's length */
352 /*
353 * Leave some extra room in case a higher protocol
354 * (IP) wants to do a pullup. Maybe we can keep
355 * someone from having to allocate another buffer
356 * a do a larger memory copy.
357 */
358 len = MIN ( ENI_SMALL_BSIZE, pdulen );
359 (void) eni_set_dma ( eup, 1, dma, TEMP_DMA_SIZE, &j,
360 vcc, (u_long)up, len );
361 /*
362 * Adjust length of remaining data in PDU
363 */
364 pdulen -= len;
365 /*
366 * Set buffer length, including our overhead
367 */
368 KB_LEN ( m ) = len + 3 * sizeof ( u_long );
369 /*
370 * Finish by moving anything which won't fit in
371 * first buffer
372 */
373 m0 = m;
374 while ( pdulen ) {
375 KBuffer *m1;
376 u_long data_addr;
377
378 /*
379 * Get another buffer
380 */
381 KB_ALLOCEXT ( m1, ENI_LARGE_BSIZE, KB_F_NOWAIT,
382 KB_T_DATA );
383
384 /*
385 * If we succeeded...
386 */
387 if ( m1 ) {
388 /*
389 * Figure out how much we can move into
390 * this buffer.
391 */
392 len = MIN ( ENI_LARGE_BSIZE, pdulen );
393 /*
394 * Setup DMA list for this buffer
395 */
396 KB_DATASTART ( m1, data_addr, u_long );
397 (void) eni_set_dma
398 ( eup, 1, dma, TEMP_DMA_SIZE, &j, vcc,
399 data_addr, len );
400 /*
401 * Adjust remaining length
402 */
403 pdulen -= len;
404 /*
405 * Set buffer length
406 */
407 KB_LEN ( m1 ) = len;
408 /*
409 * Link new buffer onto end and advance
410 * pointer
411 */
412 KB_NEXT ( m0 ) = m1;
413 m0 = m1;
414 } else {
415 /*
416 * Either we were unable to grab another
417 * buffer or there are no large buffers
418 * available. We know that the first
419 * buffer is valid, so drop everything
420 * else, build a JK DMA to skip/drop this
421 * PDU, set the pointers to reclaim
422 * resources/advance pointers, and
423 * finish this PDU now.
424 */
425 if ( KB_NEXT ( m ) )
426 KB_FREEALL ( KB_NEXT ( m ) );
427 eup->eu_pif.pif_ierrors++;
428 j = 2;
429 dma[j++] =
430 (((rdptr + n_cells*WORDS_PER_CELL + 1)
431 & (mask-1)) << DMA_COUNT_SHIFT ) |
432 (vcc << DMA_VCC_SHIFT ) |
433 DMA_JK;
434 dma[j++] = 0;
435 /*
436 * Reset PDU length to zero
437 */
438 KB_PLENSET ( m, 0 );
439 /*
440 * Count some statistics
441 */
442 /*
443 * Count this as dropped cells
444 */
445 if ( aal5 ) {
446 eup->eu_stats.eni_st_aal5.aal5_drops +=
447 n_cells;
448 eup->eu_stats.eni_st_aal5.aal5_pdu_drops++;
449 } else
450 eup->eu_stats.eni_st_aal0.aal0_drops +=
451 n_cells;
452 /*
453 * Drop it
454 */
455 goto send_dma;
456 }
457 }
458 /*
459 * If necessary, skip AAL layer
460 */
461 if ( aal5 ) {
462 dma[j++] =
463 (((rdptr + n_cells*WORDS_PER_CELL + 1)
464 & (mask-1)) << DMA_COUNT_SHIFT)
465 | (vcc << DMA_VCC_SHIFT) | DMA_JK;
466 dma[j++] = 0;
467 }
468 } else {
469 /*
470 * We failed to get an initial buffer. Since we
471 * haven't changed anything for this PDU yet and the
472 * PDU is still valid, exit now and try to service it
473 * next time around. We're not very likely to get
474 * another buffer right now anyways.
475 */
476 eup->eu_stats.eni_st_drv.drv_rv_nobufs++;
477#ifdef DO_LOG
478 log ( LOG_ERR,
479"eni_do_service: No buffers available. Exiting without servicing service list.\n" );
480#endif
481 /*
482 * Clear the IN_SERVICE indicator for this VCC
483 */
484 vct->vci_control &= ~VCI_IN_SERVICE;
485 return;
486 }
487
488send_dma:
489 /*
490 * Set the end bit on the last DMA for this PDU
491 */
492 dma[j-2] |= DMA_END_BIT;
493
494 /*
495 * Where are the current DMA pointers
496 */
497 dma_rd = eup->eu_midway[MIDWAY_RX_RD];
498 dma_wr = eup->eu_midway[MIDWAY_RX_WR];
499
500 /*
501 * Check how much space is available
502 */
503 if ( dma_rd == dma_wr )
504 dma_avail = DMA_LIST_SIZE;
505 else
506 dma_avail = ( dma_rd + DMA_LIST_SIZE - dma_wr )
507 & (DMA_LIST_SIZE-1);
508
509 /*
510 * Check for queue full or wrap past write okay pointer
511 */
512 if ( dma_avail < j ||
513 ( dma_wr + j > eup->eu_rxdmawr + DMA_LIST_SIZE ) ) {
514 /*
515 * There's no room in the DMA list to insert
516 * this request. Since we haven't changed anything
517 * yet and the PDU is good, exit now and service
518 * it next time around. What we really need to do
519 * is wait for the RX list to drain and that won't
520 * happen if we keep trying to process PDUs here.
521 */
522 eup->eu_stats.eni_st_drv.drv_rv_nodma++;
523#ifdef DO_LOG
524 log ( LOG_ERR,
525"eni_do_service: No room in receive DMA list. Postponing service request.\n" );
526#endif
527 /*
528 * Free the local buffer chain
529 */
530 KB_FREEALL ( m );
531 /*
532 * Clear the IN_SERVICE indicator for this VCC.
533 */
534 vct->vci_control &= ~VCI_IN_SERVICE;
535 return;
536 }
537
538 /*
539 * If we have a buffer chain, save the starting
540 * dma_list location.
541 */
542 if ( upp ) {
543 *upp = dma_wr << 16;
544 }
545
546 /*
547 * Stuff the DMA list
548 */
549 j >>= 1;
550 for ( i = 0; i < j; i++ ) {
551 eup->eu_rxdma[dma_wr*2] = dma[i*2];
552 eup->eu_rxdma[dma_wr*2+1] = dma[i*2+1];
553 dma_wr = (dma_wr+1) & (DMA_LIST_SIZE-1);
554 }
555 /*
556 * If we have a buffer chain, save the location of
557 * the ending dma_list location and queue the chain
558 * so that we can recover the resources later.
559 */
560 if ( upp ) {
561 *upp |= dma_wr;
562 /*
563 * Place buffer on receive queue waiting for RX_DMA
564 */
565 if ( IF_QFULL ( &eup->eu_rxqueue ) ) {
566 /*
567 * We haven't done anything we can't back out
568 * of. Drop request and service it next time.
569 * We've inserted the DMA list but it's not
570 * valid until we advance the RX_WR pointer,
571 * thus it's okay to bail here...
572 */
573 eup->eu_stats.eni_st_drv.drv_rv_rxq++;
574#ifdef DO_LOG
575 log ( LOG_ERR,
576 "eni_do_service: RX drain queue full. Postponing servicing.\n" );
577#endif
578 KB_FREEALL ( m );
579 /*
580 * Clear the IN_SERVICE indicator for this VCC.
581 */
582 vct->vci_control &= ~VCI_IN_SERVICE;
583 return;
584 } else {
585 IF_ENQUEUE ( &eup->eu_rxqueue, m );
586 /*
587 * Advance the RX_WR pointer to cause
588 * the adapter to work on this DMA list.
589 */
590 eup->eu_midway[MIDWAY_RX_WR] = dma_wr;
591 }
592 }
593 /*
594 * Advance our notion of where the next PDU
595 * should start.
596 */
597 rdptr = (rdptr + n_cells*WORDS_PER_CELL + 1)
598 & (mask-1);
599 evp->ev_rxpos = rdptr;
600
601 /*
602 * Increment cells/pdu received stats.
603 */
604 eup->eu_stats.eni_st_atm.atm_rcvd += n_cells;
605 if ( aal5 ) {
606 eup->eu_stats.eni_st_aal5.aal5_rcvd += n_cells;
607 eup->eu_stats.eni_st_aal5.aal5_pdu_rcvd++;
608 } else {
609 eup->eu_stats.eni_st_aal0.aal0_rcvd += n_cells;
610 }
611
612 /*
613 * Continue processing PDUs on this same VCI
614 */
615 }
616
617next_vcc:
618 /*
619 * Advance to next entry in the service_list.
620 */
621 eup->eu_servread = (eup->eu_servread + 1) & SVC_SIZE_MASK;
622
623 /*
624 * And clear the IN_SERVICE indicator for this VCC.
625 */
626 vct->vci_control &= ~VCI_IN_SERVICE;
627 }
628 return;
629}
630
631/*
632 * Drain Receive queue
633 *
634 * As we build DMA lists to move PDUs from adapter buffers into host
635 * buffers, we place the request on a private ifqueue so that we can
636 * free any resources AFTER we know they've been successfully DMAed.
637 * As part of the service processing, we record the PDUs start and stop
638 * entries in the DMA list, and prevent wrapping. When we pull the top
639 * entry off, we simply check that the current DMA location is outside
640 * this PDU and if so, it's okay to free things.
641 *
642 * Arguments:
643 * eup pointer to device unit structure
644 *
645 * Returns:
646 * none
647 *
648 */
649void
650eni_recv_drain ( eup )
651 Eni_unit *eup;
652{
653 KBuffer *m;
654 Eni_vcc *evp;
655 struct vccb *vcp;
656 u_long vcc;
657 u_long DMA_Rdptr;
658 u_long dma_wrp;
659 u_long start, stop;
660 int que = 0;
661 int s;
662
663 s = splimp();
664 /* Pop first buffer */
665 IF_DEQUEUE ( &eup->eu_rxqueue, m );
666 while ( m ) {
667 u_long *up;
668 u_long pdulen;
669
670 KB_DATASTART ( m, up, u_long * );
671
672 /*
673 * Grab the VCI number
674 */
675 vcc = *up++;
676
677 /*
678 * Check to see if we can process this buffer yet.
679 */
680 /* Get current DMA_Rdptr */
681 DMA_Rdptr = eup->eu_midway[MIDWAY_RX_RD];
682 /* Boundaries for first buffer */
683 dma_wrp = *up++;
684 start = dma_wrp >> 16;
685 stop = dma_wrp & 0xffff;
686 /*
687 * Start should not equal stop because that would
688 * mean we tried inserting a NULL DMA list.
689 */
690 if ( start > stop ) { /* We wrapped */
691 if ( !(DMA_Rdptr >= stop && DMA_Rdptr < start) ) {
692 IF_PREPEND ( &eup->eu_rxqueue, m );
693 goto finish;
694 }
695 } else {
696 if ( DMA_Rdptr < stop && DMA_Rdptr >= start ) {
697 IF_PREPEND ( &eup->eu_rxqueue, m );
698 goto finish;
699 }
700 }
701 /*
702 * Adapter is finished with this buffer, we can
703 * continue processing it now.
704 */
705
706 /*
707 * Locate incoming VCC for this PDU
708 */
709 evp = (Eni_vcc *) atm_dev_vcc_find ( (Cmn_unit *)eup,
710 0, vcc, VCC_IN );
711
712 if ( evp == NULL ) {
713 eup->eu_stats.eni_st_drv.drv_rv_novcc++;
714 KB_FREEALL ( m );
715 goto next_buffer;
716 }
717
718#ifdef DIAGNOSTIC
719 if ( eni_pdu_print )
720 atm_dev_pdu_print ( (Cmn_unit *)eup, (Cmn_vcc *)evp, m,
721 "eni_stack_drain" );
722#endif
723
724 /*
725 * Grab theoretical PDU length
726 */
727 pdulen = *up++;
728
729 /*
730 * Quick, count the PDU
731 */
732 eup->eu_pif.pif_ipdus++;
733 eup->eu_pif.pif_ibytes += pdulen;
734 if ( evp ) {
735 vcp = evp->ev_connvc->cvc_vcc;
736 if ( vcp ) {
737 vcp->vc_ipdus++;
738 vcp->vc_ibytes += pdulen;
739 if ( vcp->vc_nif ) {
740 vcp->vc_nif->nif_ibytes += pdulen;
741 vcp->vc_nif->nif_if.if_ipackets++;
742#if (defined(BSD) && (BSD >= 199103))
743 vcp->vc_nif->nif_if.if_ibytes += pdulen;
744#endif
745 }
746 }
747 }
748
749 /*
750 * Advance DMA write allowable pointer
751 */
752 eup->eu_rxdmawr = stop;
753
754 /*
755 * Get packet PDU length
756 */
757 KB_PLENGET ( m, pdulen );
758
759 /*
760 * Only try queueing this if there is data
761 * to be handed up to the next layer. Errors
762 * such as CRC and VC trashing will get us this
763 * far to advance pointers, etc., but the PDU
764 * length will be zero.
765 */
766 if ( pdulen ) {
767 /*
768 * We saved three words back in eni_do_service()
769 * to use for callback. Since the core only
770 * expects two words, skip over the first one.
771 * Then, reset up pointer to start of buffer data
772 * area and write the callback info.
773 */
774 KB_HEADADJ ( m, -sizeof(u_long) );
775 KB_DATASTART ( m, up, u_long * );
776 *((int *)up) = (int)eni_recv_stack;
777 up++;
778 *((int *)up) = (int)evp;
779 /*
780 * Schedule callback
781 */
782 if ( !IF_QFULL ( &atm_intrq ) ) {
783 que++;
784 IF_ENQUEUE ( &atm_intrq, m );
785 } else {
786 eup->eu_stats.eni_st_drv.drv_rv_intrq++;
787 eup->eu_pif.pif_ierrors++;
788#ifdef DO_LOG
789 log ( LOG_ERR,
790"eni_receive_drain: ATM_INTRQ is full. Unable to pass up stack.\n" );
791#endif
792 KB_FREEALL ( m );
793 }
794 } else {
795 /*
796 * Free zero-length buffer
797 */
798 KB_FREEALL(m);
799 }
800
801next_buffer:
802 /*
803 * Look for next buffer
804 */
805 IF_DEQUEUE ( &eup->eu_rxqueue, m );
806 }
807finish:
808 (void) splx(s);
809
810 /*
811 * If we found any completed buffers, schedule a call into
812 * the kernel to process the atm_intrq.
813 */
814 if ( que )
815 SCHED_ATM;
816
817 return;
818
819}
820
821/*
822 * Pass incoming PDU up Stack
823 *
824 * This function is called via the core ATM interrupt queue callback
825 * set in eni_recv_drain(). It will pass the supplied incoming
826 * PDU up the incoming VCC's stack.
827 *
828 * Arguments:
829 * tok token to identify stack instantiation
830 * m pointer to incoming PDU buffer chain
831 *
832 * Returns:
833 * none
834 */
835static void
836eni_recv_stack ( tok, m )
837 void *tok;
838 KBuffer *m;
839{
840 Eni_vcc *evp = (Eni_vcc *)tok;
841 int err;
842
843 /*
844 * This should never happen now but if it does and we don't stop it,
845 * we end up panic'ing in netatm when trying to pull a function
846 * pointer and token value out of a buffer with address zero.
847 */
848 if ( !m ) {
849#ifdef DO_LOG
850 log ( LOG_ERR,
851 "eni_recv_stack: NULL buffer, tok = %p\n", tok );
852#endif
853 return;
854 }
855
856 /*
857 * Send the data up the stack
858 */
859 STACK_CALL ( CPCS_UNITDATA_SIG, evp->ev_upper,
860 (void *)evp->ev_toku, evp->ev_connvc, (int)m, 0, err );
861 if ( err ) {
862 KB_FREEALL ( m );
863 }
864
865 return;
866}
867