📄 eni_transmit.c
字号:
long j; u_long dma_avail; u_long dma_start; Eni_mem tx_send; u_long *up; KBuffer *m0 = m, *m1, *mprev = NULL; caddr_t cp, bfr; u_int len, align; int compressed = 0;#ifdef DIAGNOSTIC if ( eni_pdu_print ) atm_dev_pdu_print ( cup, cvp, m, "eni output" );#endif /* * Re-entry point for after buffer compression (if needed) */retry: /* * We can avoid traversing the buffer list twice by building * the middle (minus header and trailer) dma list at the * same time we massage address and size alignments. Since * this list remains local until we determine we've enough * room, we're not going to trash anything by not checking * sizes, etc. yet. Skip first entry to be used later to skip * descriptor word. */ j = 2; /* * Do data positioning for address and length alignment */ while ( m ) { u_long buf_addr; /* For passing addr to eni_set_dma() */ /* * Get rid of any zero length buffers */ if ( KB_LEN ( m ) == 0 ) { if ( mprev ) { KB_UNLINK ( m, mprev, m1 ); } else { KB_UNLINKHEAD ( m, m1 ); m0 = m1; } m = m1; continue; } /* * Get start of data onto full-word alignment */ KB_DATASTART ( m, cp, caddr_t ); if ( align = ((u_int)cp) & (sizeof(u_long)-1)) { /* * Gotta slide the data up */ eup->eu_stats.eni_st_drv.drv_xm_segnoal++; bfr = cp - align; KM_COPY ( cp, bfr, KB_LEN ( m ) ); KB_HEADMOVE ( m, -align ); } else { /* * Data already aligned */ bfr = cp; } /* * Now work on getting the data length correct */ len = KB_LEN ( m ); while ( ( align = ( len & (sizeof(u_long)-1))) && (m1 = KB_NEXT ( m ) ) ) { /* * Have to move some data from following buffer(s) * to word-fill this buffer */ u_int ncopy = MIN ( sizeof(u_long) - align, KB_LEN ( m1 ) ); if ( ncopy ) { /* * Move data to current buffer */ caddr_t dest; eup->eu_stats.eni_st_drv.drv_xm_seglen++; KB_DATASTART ( m1, cp, caddr_t ); dest = bfr + len; KB_HEADADJ ( m1, -ncopy ); KB_TAILADJ ( m, ncopy ); len += ncopy; while ( ncopy-- ) { *dest++ = *cp++; } } /* * If we've drained the buffer, free it */ if ( KB_LEN ( m1 ) == 0 ) { KBuffer *m2; KB_UNLINK ( m1, m, m2 ); } } /* * Address and size are now aligned. Build dma list * using TX channel 0. Also, round length up to a word * size which should only effect the last buffer in the * chain. This works because the PDU length is maintained * seperately and we're not really adjusting the buffer's * idea of its length. */ KB_DATASTART ( m, buf_addr, u_long ); if ( eni_set_dma ( eup, 0, dma, TEMP_DMA_SIZE, &j, 0, buf_addr, KB_LEN ( m ) ) < 0 ) { /* * Failed to build DMA list. First, we'll try to * compress the buffer chain into a smaller number * of buffers. After compressing, we'll try to send * the new buffer chain. If we still fail, then * we'll drop the pdu. */ if ( compressed ) {#ifdef DO_LOG log ( LOG_ERR, "eni_output: eni_set_dma failed\n" );#endif eup->eu_pif.pif_oerrors++; KB_FREEALL ( m0 ); return; } eup->eu_stats.eni_st_drv.drv_xm_maxpdu++; m = atm_dev_compress ( m0 ); if ( m == NULL ) {#ifdef DO_LOG log ( LOG_ERR, "eni_output: atm_dev_compress() failed\n" );#endif eup->eu_pif.pif_oerrors++; return; } /* * Reset to new head of buffer chain */ m0 = m; /* * Indicate we've been through here */ compressed = 1; /* * Retry to build the DMA descriptors for the newly * compressed buffer chain */ goto retry; } /* * Now count the length */ pdulen += KB_LEN ( m ); /* * Bump counters and get ready for next buffer */ mprev = m; m = KB_NEXT ( m ); } /* * Get a buffer to use in a private queue so that we can * reclaim resources after the DMA has finished. */ KB_ALLOC ( m, ENI_SMALL_BSIZE, KB_F_NOWAIT, KB_T_DATA ); if ( m ) { /* * Link the PDU onto our new head */ KB_NEXT ( m ) = m0; } else { /* * Drop this PDU and let the sender try again. */ eup->eu_stats.eni_st_drv.drv_xm_norsc++;#ifdef DO_LOG log(LOG_ERR, "eni_output: Unable to allocate drain buffer.\n");#endif eup->eu_pif.pif_oerrors++; KB_FREEALL ( m0 ); return; } s = splnet(); /* * Calculate size of buffer necessary to store PDU. If this * is an AAL5 PDU, we'll need to know where to stuff the length * value in the trailer. */ /* * AAL5 PDUs need an extra two words for control/length and * CRC. Check for AAL5 and add requirements here. */ if (aal5 = (evp->ev_connvc->cvc_attr.aal.type == ATM_AAL5)) size = pdulen + 2 * sizeof(long); else size = pdulen; /* * Pad to next complete cell boundary */ size += (BYTES_PER_CELL - 1); size -= size % BYTES_PER_CELL; /* * Convert size to words and add 2 words overhead for every * PDU (descriptor and cell header). */ size = (size >> 2) + 2; /* * First, check to see if there's enough buffer space to * store the PDU. We do this by checking to see if the size * required crosses the eu_txfirst pointer. However, we don't * want to exactly fill the buffer, because we won't be able to * distinguish between a full and empty buffer. */ if ( eup->eu_txpos == eup->eu_txfirst ) buf_avail = eup->eu_txsize; else if ( eup->eu_txpos > eup->eu_txfirst ) buf_avail = eup->eu_txsize - ( eup->eu_txpos - eup->eu_txfirst ); else buf_avail = eup->eu_txfirst - eup->eu_txpos; if ( size >= buf_avail ) { /* * No buffer space in the adapter to store this PDU. * Drop PDU and return. */ eup->eu_stats.eni_st_drv.drv_xm_nobuf++;#ifdef DO_LOG log ( LOG_ERR, "eni_output: not enough room in buffer\n" );#endif eup->eu_pif.pif_oerrors++; KB_FREEALL ( m ); (void) splx(s); return; } /* * Find out where current DMA pointers are at */ dma_start = dma_wr = eup->eu_midway[MIDWAY_TX_WR]; dma_rd = eup->eu_midway[MIDWAY_TX_RD]; /* * Figure out how much DMA room we have available */ if ( dma_rd == dma_wr ) { /* Queue is empty */ dma_avail = DMA_LIST_SIZE; } else { dma_avail = ( dma_rd + DMA_LIST_SIZE - dma_wr ) & ( DMA_LIST_SIZE - 1 ); } /* * Check to see if we can describe this PDU or if we're: * out of room, will wrap past recovered resources. */ if ( dma_avail < (j / 2 + 4) || ( dma_wr + (j / 2 + 4) > eup->eu_txdmawr + DMA_LIST_SIZE ) ) { /* * No space to insert DMA list into queue. Drop this PDU. */ eup->eu_stats.eni_st_drv.drv_xm_nodma++;#ifdef DO_LOG log ( LOG_ERR, "eni_output: not enough room in DMA queue\n" );#endif eup->eu_pif.pif_oerrors++; KB_FREEALL( m ); (void) splx(s); return; } /* * Create DMA descriptor for header. There is a descriptor word * and also a cell header word which we'll set manually. */ dma[0] = (((int)(eup->eu_txpos + 2) & (eup->eu_txsize-1)) << DMA_COUNT_SHIFT) | DMA_JK; dma[1] = 0; /* * JK for AAL5 trailer. Set END bit as well. */ if ( aal5 ) { dma[j++] = (((int)(eup->eu_txpos+size) & (eup->eu_txsize-1)) << DMA_COUNT_SHIFT) | DMA_END_BIT | DMA_JK; dma[j++] = 0; } else { dma[j-2] |= DMA_END_BIT; /* Backup and set END bit */ } /* * Find out where in adapter memory this TX buffer starts. */ tx_send = (Eni_mem) ((((int)eup->eu_midway[MIDWAY_TXPLACE] & 0x7ff) << ENI_LOC_PREDIV) + (int)eup->eu_ram); /* * Set descriptor word */ tx_send[eup->eu_txpos] = (MIDWAY_UNQ_ID << 28) | (aal5 ? 1 << 27 : 0) | (size / WORDS_PER_CELL); /* * Set cell header */ tx_send[(eup->eu_txpos+1)&(eup->eu_txsize-1)] = evp->ev_connvc->cvc_vcc->vc_vci << 4; /* * We've got all our resources, count the stats */ if ( aal5 ) { /* * If this is an AAL5 PDU, we need to set the length */ tx_send[(eup->eu_txpos+size-2) & (eup->eu_txsize-1)] = pdulen; /* * Increment AAL5 stats */ eup->eu_stats.eni_st_aal5.aal5_pdu_xmit++; eup->eu_stats.eni_st_aal5.aal5_xmit += (size - 2) / WORDS_PER_CELL; } else { /* * Increment AAL0 stats */ eup->eu_stats.eni_st_aal0.aal0_xmit += (size - 2) / WORDS_PER_CELL; } /* * Increment ATM stats */ eup->eu_stats.eni_st_atm.atm_xmit += (size - 2) / WORDS_PER_CELL; /* * Store the DMA list */ j = j >> 1; for ( i = 0; i < j; i++ ) { eup->eu_txdma[dma_wr*2] = dma[i*2]; eup->eu_txdma[dma_wr*2+1] = dma[i*2+1]; dma_wr = (dma_wr+1) & (DMA_LIST_SIZE-1); } /* * Build drain buffer * * We toss four words in to help keep track of this * PDU. The first is a pointer to the VC control block * so we can find which VCI this went out on, the second * is the start and stop pointers for the DMA list which * describes this PDU, the third is the PDU length * since we'll want to know that for stats gathering, * and the fourth is the number of DMA words. */ KB_DATASTART ( m, up, u_long * ); *up++ = (u_long)cvp; *up++ = dma_start << 16 | dma_wr; *up++ = pdulen; *up = size; /* * Set length of our buffer */ KB_LEN ( m ) = 4 * sizeof ( long ); /* * Place buffers onto transmit queue for draining */ s2 = splimp(); IF_ENQUEUE ( &eup->eu_txqueue, m ); (void) splx(s2); /* * Update next word to be stored */ eup->eu_txpos = ((eup->eu_txpos + size) & (eup->eu_txsize - 1)); /* * Update MIDWAY_TX_WR pointer */ eup->eu_midway[MIDWAY_TX_WR] = dma_wr; (void) splx ( s ); return;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -