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