eni_receive.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 872 行 · 第 1/2 页
C
872 行
/* * * =================================== * HARP | Host ATM Research Platform * =================================== * * * This Host ATM Research Platform ("HARP") file (the "Software") is * made available by Network Computing Services, Inc. ("NetworkCS") * "AS IS". NetworkCS does not provide maintenance, improvements or * support of any kind. * * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. * In no event shall NetworkCS be responsible for any damages, including * but not limited to consequential damages, arising from or relating to * any use of the Software or related support. * * Copyright 1994-1998 Network Computing Services, Inc. * * Copies of this Software may be made, however, the above copyright * notice must be reproduced on all copies. * * @(#) $Id: eni_receive.c,v 1.3 1998/10/31 20:06:45 phk Exp $ * *//* * Efficient ENI Adapter Support * ----------------------------- * * Receive management * */#include <netatm/kern_include.h>#include <dev/hea/eni_stats.h>#include <dev/hea/eni.h>#include <dev/hea/eni_var.h>#ifndef lint__RCSID("@(#) $Id: eni_receive.c,v 1.3 1998/10/31 20:06:45 phk Exp $");#endifstatic void eni_recv_stack __P((void *, KBuffer *));#ifdef DIAGNOSTICextern int eni_pdu_print;#endif/* * Procedure to remove VCs from the Service List and generate DMA * requests to move the associated PDUs into host memory. As PDUs * are completed in adapter memory, the adapter examines the IN_SERVICE * bit for the VC in the VC table. If this bit is not set, the adapter * will place the VC number at the end of the service list queue, set * the IN_SERVICE bit in the VC table, and interrupt the host. The host * will remove VCs from the service list, clear the IN_SERVICE bit in * the VC table, and create a DMA list to move the PDU into host buffers. * * Arguments: * eup pointer to per unit structure * * Returns: * none * */voideni_do_service ( eup ) Eni_unit *eup;{ int vcc; Eni_vcc *evp; u_long servwrite; VCI_Table *vct; u_long rdptr; u_long *rxp; KBuffer *m; u_long dma[TEMP_DMA_SIZE]; u_long i, j; u_long dma_rd, dma_wr; u_long dma_avail; int pdulen; int mask; u_long *upp; /* * Where is the adapter currently inserting entries? */ servwrite = eup->eu_midway[MIDWAY_SVCWR] & SVC_SIZE_MASK; /* * As long as we're not caught up with the adapter, keep * removing VCs from the service list. */ while ( servwrite != eup->eu_servread ) { int vci_hdr; u_long descr; /* * Get VC number and find VC table entry. */ vcc = eup->eu_svclist[eup->eu_servread]; vct = &eup->eu_vcitbl[vcc]; vci_hdr = vct->vci_control; /* Current status */ /* * Check that this VCC still needs servicing. We * might have closed this VCC down in between * the adapter setting the flag and our checking * the flag. Also check that we haven't placed the * VCC into TRASH mode. */ if ( ( vci_hdr & VCI_IN_SERVICE ) == 0 || ( vci_hdr & ~VCI_MODE_MASK == VCI_MODE_TRASH << VCI_MODE_SHIFT ) ) goto next_vcc; /* * Find the size of this VCs buffer */ mask = (vci_hdr >> VCI_SIZE_SHIFT) & VCI_SIZE_MASK; mask = 1 << (ENI_LOC_PREDIV + mask); /* Turn byte count into word count */ mask >>= 2; /* * Find the start of the adapter buffer for this VC. */ rxp = (u_long *) ((int)(((vci_hdr >> VCI_LOC_SHIFT ) & VCI_LOC_MASK) << ENI_LOC_PREDIV) + (int)eup->eu_ram); /* * Locate incoming VCC for this PDU and find where we * should next read from. */ evp = (Eni_vcc *) atm_dev_vcc_find ( (Cmn_unit *)eup, 0, vcc, VCC_IN ); if ( evp == (Eni_vcc *)NULL ) goto next_vcc; /* VCI no longer active */ rdptr = evp->ev_rxpos; /* * Find out where the adapter is currently reassembling. * The PDU which starts at descr is not yet complete so we * must stop there. */ descr = ( vct->vci_descr >> 16 ) & 0x7FFF; /* * As long as we haven't processed all the completed PDUs on * this VC, keep going... */ while ( rdptr != descr ) { int n_cells; int pdu_descr; int aal5; /* * Ensure that the following are reset for every new * PDU. */ upp = NULL; m = NULL; /* * Fisrt build a DMA with JK to skip the descriptor word. * We must always skip the descriptor even if it turns out * that there isn't any PDU here. */ j = 0; dma[j++] = (((rdptr + 1) & (mask-1)) << DMA_COUNT_SHIFT ) | ( vcc << DMA_VCC_SHIFT ) | DMA_JK; dma[j++] = 0; /* * We'll use some of the values below for skipping * bad PDUs or counting statistics so compute them * now. */ /* * Grab a copy of the descriptor word */ pdu_descr = rxp[rdptr]; /* * Strip out cell count from descriptor word. * At this point, we still don't know if there * is any real data until after we check for * TRASH mode. */ n_cells = pdu_descr & DESCR_CELL_COUNT; /* * Is this an AAL5 PDU? Check MODE in vci_hdr. */ aal5 = ( ( vci_hdr & ~VCI_MODE_MASK ) == VCI_MODE_AAL5 << VCI_MODE_SHIFT ); /* * Now check to see if we're trashing on this vcc. * If so, there is no data with this VC and the * next word after the current descriptor is the * descriptor for the next PDU. */ if ( ( pdu_descr & DESCR_TRASH_BIT ) != 0 ) { if ( aal5 ) /* * Count as number of AAL5 cells dropped */ eup->eu_stats.eni_st_aal5.aal5_drops += n_cells; else /* * Count as number of AAL0 cells dropped */ eup->eu_stats.eni_st_aal0.aal0_drops += n_cells; eup->eu_pif.pif_ierrors++; /* * When cells have been trashed, all we have in the * buffer is a descriptor word. There are no data * words. Set the number of cells to zero so that * we correctly skip to the next word which will * be the descriptor for the next PDU. */ n_cells = 0; /* * Go issue the DMA to skip this descriptor word. */ goto send_dma; } /* * Data length: number of cells * cell size */ pdulen = n_cells * BYTES_PER_CELL; /* * If this is an AAL5 PDU, then we need to check * for the presence of any CRC errors. If there * is one or more CRC errors, then we are going to * drop this PDU. */ if ( aal5 && ( pdu_descr & DESCR_CRC_ERR ) ) { /* * Count the stat */ eup->eu_pif.pif_ierrors++; eup->eu_stats.eni_st_aal5.aal5_pdu_crc++; if ( evp->ev_connvc->cvc_vcc ) evp->ev_connvc->cvc_vcc->vc_ierrors++; /* * Build a DMA entry to skip the rest of this * PDU. */ dma[j++] = (((rdptr + n_cells*WORDS_PER_CELL + 1) & (mask-1)) << DMA_COUNT_SHIFT ) | (vcc << DMA_VCC_SHIFT ) | DMA_JK; dma[j++] = 0; /* * All done with this PDU. Get a buffer to save some * data for reclamation services. */ KB_ALLOCPKT ( m, ENI_SMALL_BSIZE, KB_F_NOWAIT, KB_T_DATA ); if ( m ) { u_long *up; KB_DATASTART ( m, up, u_long * ); /* * Indicate no PDU */ KB_PLENSET ( m, 0 ); /* * Set buffer length - only driver overhead */ KB_LEN ( m ) = 3 * sizeof ( u_long ); /* * Insert vcc, space for DMA pointers, * and pdulen */ *up++ = vcc; upp = up; /* Remember location */ up++; /* And skip it */ /* - to be filled later */ *up = pdulen; /* Actual PDU length if it */ /* were valid */ } else { /* * We've a real problem here as now we can't * reclaim/advance resources/safety pointers. */ eup->eu_stats.eni_st_drv.drv_rv_norsc++;#ifdef DO_LOG log ( LOG_ERR, "eni_do_service: No drain buffers available. Receiver about to lock.\n" );#endif } goto send_dma; } /* * Do we need to strip the AAL layer? Yes if this * is an AAL5 PDU. */ if ( aal5 ) { /* * Grab the CS-PDU length. Find the address of the * last word, back up one word to skip CRC, and * then mask the whole thing to handle circular wraps. */ pdulen = rxp[(rdptr + n_cells*WORDS_PER_CELL - 1) & (mask-1)] & 0xFFFF; } /* * We now have a valid PDU of some length. Build * the necessary DMA list to move it into host * memory. */ /* * Get an initial buffer. */ KB_ALLOCPKT ( m, ENI_SMALL_BSIZE, KB_F_NOWAIT, KB_T_DATA ); /* * Do we have a valid buffer? */ if ( m != (KBuffer *)NULL ) { int len; u_long *up; KBuffer *m0; KB_DATASTART ( m, up, u_long * ); /* * Fill in pdulen in PKTHDR structure (for IP). */ KB_PLENSET ( m, pdulen ); /* * We're going to save the VCI nuber, the start * and stop DMA pointers, and the PDU length at * the head of the buffer. We'll pull this out * later after the DMA has completed. * * Insert VCI number as first word in first buffer, * remeber where we want to store the start/stop * pointers, and store the PDU length. */ *up++ = vcc; /* PDU's VCC */ upp = up; /* Remember where we are */ up++; /* To stuff start/stop pointers in */ *up++ = pdulen; /* PDU's length */ /* * Leave some extra room in case a higher protocol * (IP) wants to do a pullup. Maybe we can keep * someone from having to allocate another buffer * a do a larger memory copy. */ len = MIN ( ENI_SMALL_BSIZE, pdulen ); (void) eni_set_dma ( eup, 1, dma, TEMP_DMA_SIZE, &j, vcc, (u_long)up, len ); /* * Adjust length of remaining data in PDU */ pdulen -= len; /* * Set buffer length, including our overhead */ KB_LEN ( m ) = len + 3 * sizeof ( u_long ); /* * Finish by moving anything which won't fit in * first buffer */ m0 = m; while ( pdulen ) { KBuffer *m1; u_long data_addr; /* * Get another buffer */ KB_ALLOCEXT ( m1, ENI_LARGE_BSIZE, KB_F_NOWAIT, KB_T_DATA ); /* * If we succeeded... */ if ( m1 ) { /* * Figure out how much we can move into * this buffer. */ len = MIN ( ENI_LARGE_BSIZE, pdulen ); /* * Setup DMA list for this buffer */ KB_DATASTART ( m1, data_addr, u_long ); (void) eni_set_dma ( eup, 1, dma, TEMP_DMA_SIZE, &j, vcc, data_addr, len ); /* * Adjust remaining length */ pdulen -= len; /* * Set buffer length */ KB_LEN ( m1 ) = len; /* * Link new buffer onto end and advance * pointer */ KB_NEXT ( m0 ) = m1; m0 = m1; } else { /* * Either we were unable to grab another * buffer or there are no large buffers * available. We know that the first * buffer is valid, so drop everything * else, build a JK DMA to skip/drop this * PDU, set the pointers to reclaim * resources/advance pointers, and * finish this PDU now. */ if ( KB_NEXT ( m ) ) KB_FREEALL ( KB_NEXT ( m ) ); eup->eu_pif.pif_ierrors++; j = 2; dma[j++] = (((rdptr + n_cells*WORDS_PER_CELL + 1) & (mask-1)) << DMA_COUNT_SHIFT ) | (vcc << DMA_VCC_SHIFT ) |
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?