3 * ===================================
4 * HARP | Host ATM Research Platform
5 * ===================================
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.
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.
21 * Copyright 1994-1998 Network Computing Services, Inc.
23 * Copies of this Software may be made, however, the above copyright
24 * notice must be reproduced on all copies.
31 * Efficient ENI Adapter Support
32 * -----------------------------
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <sys/socketvar.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>
59 #include <dev/hea/eni_stats.h>
60 #include <dev/hea/eni.h>
61 #include <dev/hea/eni_var.h>
64 __RCSID("@(#) $FreeBSD$");
67 static void eni_recv_stack(void *, KBuffer *);
70 extern int eni_pdu_print;
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.
84 * eup pointer to per unit structure
91 eni_do_service ( eup )
101 u_long dma[TEMP_DMA_SIZE];
103 u_long dma_rd, dma_wr;
110 * Where is the adapter currently inserting entries?
112 servwrite = eup->eu_midway[MIDWAY_SVCWR] & SVC_SIZE_MASK;
114 * As long as we're not caught up with the adapter, keep
115 * removing VCs from the service list.
117 while ( servwrite != eup->eu_servread ) {
122 * Get VC number and find VC table entry.
124 vcc = eup->eu_svclist[eup->eu_servread];
125 vct = &eup->eu_vcitbl[vcc];
126 vci_hdr = vct->vci_control; /* Current status */
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.
135 if ( ( vci_hdr & VCI_IN_SERVICE ) == 0 ||
136 ( (vci_hdr & ~VCI_MODE_MASK) ==
137 (VCI_MODE_TRASH << VCI_MODE_SHIFT) ) )
141 * Find the size of this VCs buffer
143 mask = (vci_hdr >> VCI_SIZE_SHIFT) & VCI_SIZE_MASK;
144 mask = 1 << (ENI_LOC_PREDIV + mask);
145 /* Turn byte count into word count */
148 * Find the start of the adapter buffer for this VC.
151 ((intptr_t)(((vci_hdr >> VCI_LOC_SHIFT ) & VCI_LOC_MASK)
152 << ENI_LOC_PREDIV) + (intptr_t)eup->eu_ram);
154 * Locate incoming VCC for this PDU and find where we
155 * should next read from.
157 evp = (Eni_vcc *) atm_dev_vcc_find ( (Cmn_unit *)eup,
159 if ( evp == (Eni_vcc *)NULL )
160 goto next_vcc; /* VCI no longer active */
161 rdptr = evp->ev_rxpos;
163 * Find out where the adapter is currently reassembling.
164 * The PDU which starts at descr is not yet complete so we
167 descr = ( vct->vci_descr >> 16 ) & 0x7FFF;
169 * As long as we haven't processed all the completed PDUs on
170 * this VC, keep going...
172 while ( rdptr != descr )
179 * Ensure that the following are reset for every new
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.
191 dma[j++] = (((rdptr + 1) & (mask-1)) << DMA_COUNT_SHIFT ) |
192 ( vcc << DMA_VCC_SHIFT ) | DMA_JK;
196 * We'll use some of the values below for skipping
197 * bad PDUs or counting statistics so compute them
202 * Grab a copy of the descriptor word
204 pdu_descr = rxp[rdptr];
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
212 n_cells = pdu_descr & DESCR_CELL_COUNT;
215 * Is this an AAL5 PDU? Check MODE in vci_hdr.
217 aal5 = ( ( vci_hdr & ~VCI_MODE_MASK ) ==
218 VCI_MODE_AAL5 << VCI_MODE_SHIFT );
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.
226 if ( ( pdu_descr & DESCR_TRASH_BIT ) != 0 ) {
229 * Count as number of AAL5 cells dropped
231 eup->eu_stats.eni_st_aal5.aal5_drops += n_cells;
234 * Count as number of AAL0 cells dropped
236 eup->eu_stats.eni_st_aal0.aal0_drops += n_cells;
237 eup->eu_pif.pif_ierrors++;
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.
247 * Go issue the DMA to skip this descriptor word.
253 * Data length: number of cells * cell size
255 pdulen = n_cells * BYTES_PER_CELL;
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
263 if ( aal5 && ( pdu_descr & DESCR_CRC_ERR ) ) {
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++;
272 * Build a DMA entry to skip the rest of this
276 (((rdptr + n_cells*WORDS_PER_CELL + 1)
277 & (mask-1)) << DMA_COUNT_SHIFT ) |
278 (vcc << DMA_VCC_SHIFT ) | DMA_JK;
281 * All done with this PDU. Get a buffer to save some
282 * data for reclamation services.
284 KB_ALLOCPKT ( m, ENI_SMALL_BSIZE, KB_F_NOWAIT,
289 KB_DATASTART ( m, up, u_long * );
295 * Set buffer length - only driver overhead
297 KB_LEN ( m ) = 3 * sizeof ( u_long );
299 * Insert vcc, space for DMA pointers,
303 upp = up; /* Remember location */
304 up++; /* And skip it */
305 /* - to be filled later */
306 *up = pdulen; /* Actual PDU length if it */
310 * We've a real problem here as now we can't
311 * reclaim/advance resources/safety pointers.
313 eup->eu_stats.eni_st_drv.drv_rv_norsc++;
316 "eni_do_service: No drain buffers available. Receiver about to lock.\n" );
323 * Do we need to strip the AAL layer? Yes if this
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.
332 pdulen = rxp[(rdptr + n_cells*WORDS_PER_CELL - 1)
338 * We now have a valid PDU of some length. Build
339 * the necessary DMA list to move it into host
344 * Get an initial buffer.
346 KB_ALLOCPKT ( m, ENI_SMALL_BSIZE, KB_F_NOWAIT, KB_T_DATA );
348 * Do we have a valid buffer?
350 if ( m != (KBuffer *)NULL )
356 KB_DATASTART ( m, up, u_long * );
358 * Fill in pdulen in PKTHDR structure (for IP).
360 KB_PLENSET ( m, pdulen );
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.
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.
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 */
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.
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 );
385 * Adjust length of remaining data in PDU
389 * Set buffer length, including our overhead
391 KB_LEN ( m ) = len + 3 * sizeof ( u_long );
393 * Finish by moving anything which won't fit in
404 KB_ALLOCEXT ( m1, ENI_LARGE_BSIZE, KB_F_NOWAIT,
412 * Figure out how much we can move into
415 len = MIN ( ENI_LARGE_BSIZE, pdulen );
417 * Setup DMA list for this buffer
419 KB_DATASTART ( m1, data_addr, u_long );
421 ( eup, 1, dma, TEMP_DMA_SIZE, &j, vcc,
424 * Adjust remaining length
432 * Link new buffer onto end and advance
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.
449 KB_FREEALL ( KB_NEXT ( m ) );
450 eup->eu_pif.pif_ierrors++;
453 (((rdptr + n_cells*WORDS_PER_CELL + 1)
454 & (mask-1)) << DMA_COUNT_SHIFT ) |
455 (vcc << DMA_VCC_SHIFT ) |
459 * Reset PDU length to zero
463 * Count some statistics
466 * Count this as dropped cells
469 eup->eu_stats.eni_st_aal5.aal5_drops +=
471 eup->eu_stats.eni_st_aal5.aal5_pdu_drops++;
473 eup->eu_stats.eni_st_aal0.aal0_drops +=
482 * If necessary, skip AAL layer
486 (((rdptr + n_cells*WORDS_PER_CELL + 1)
487 & (mask-1)) << DMA_COUNT_SHIFT)
488 | (vcc << DMA_VCC_SHIFT) | DMA_JK;
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.
499 eup->eu_stats.eni_st_drv.drv_rv_nobufs++;
502 "eni_do_service: No buffers available. Exiting without servicing service list.\n" );
505 * Clear the IN_SERVICE indicator for this VCC
507 vct->vci_control &= ~VCI_IN_SERVICE;
513 * Set the end bit on the last DMA for this PDU
515 dma[j-2] |= DMA_END_BIT;
518 * Where are the current DMA pointers
520 dma_rd = eup->eu_midway[MIDWAY_RX_RD];
521 dma_wr = eup->eu_midway[MIDWAY_RX_WR];
524 * Check how much space is available
526 if ( dma_rd == dma_wr )
527 dma_avail = DMA_LIST_SIZE;
529 dma_avail = ( dma_rd + DMA_LIST_SIZE - dma_wr )
533 * Check for queue full or wrap past write okay pointer
535 if ( dma_avail < j ||
536 ( dma_wr + j > eup->eu_rxdmawr + DMA_LIST_SIZE ) ) {
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.
545 eup->eu_stats.eni_st_drv.drv_rv_nodma++;
548 "eni_do_service: No room in receive DMA list. Postponing service request.\n" );
551 * Free the local buffer chain
555 * Clear the IN_SERVICE indicator for this VCC.
557 vct->vci_control &= ~VCI_IN_SERVICE;
562 * If we have a buffer chain, save the starting
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);
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.
586 * Place buffer on receive queue waiting for RX_DMA
588 if ( _IF_QFULL ( &eup->eu_rxqueue ) ) {
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...
596 eup->eu_stats.eni_st_drv.drv_rv_rxq++;
599 "eni_do_service: RX drain queue full. Postponing servicing.\n" );
603 * Clear the IN_SERVICE indicator for this VCC.
605 vct->vci_control &= ~VCI_IN_SERVICE;
608 _IF_ENQUEUE ( &eup->eu_rxqueue, m );
610 * Advance the RX_WR pointer to cause
611 * the adapter to work on this DMA list.
613 eup->eu_midway[MIDWAY_RX_WR] = dma_wr;
617 * Advance our notion of where the next PDU
620 rdptr = (rdptr + n_cells*WORDS_PER_CELL + 1)
622 evp->ev_rxpos = rdptr;
625 * Increment cells/pdu received stats.
627 eup->eu_stats.eni_st_atm.atm_rcvd += n_cells;
629 eup->eu_stats.eni_st_aal5.aal5_rcvd += n_cells;
630 eup->eu_stats.eni_st_aal5.aal5_pdu_rcvd++;
632 eup->eu_stats.eni_st_aal0.aal0_rcvd += n_cells;
636 * Continue processing PDUs on this same VCI
642 * Advance to next entry in the service_list.
644 eup->eu_servread = (eup->eu_servread + 1) & SVC_SIZE_MASK;
647 * And clear the IN_SERVICE indicator for this VCC.
649 vct->vci_control &= ~VCI_IN_SERVICE;
655 * Drain Receive queue
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.
666 * eup pointer to device unit structure
673 eni_recv_drain ( eup )
687 /* Pop first buffer */
688 _IF_DEQUEUE ( &eup->eu_rxqueue, m );
693 KB_DATASTART ( m, up, u_long * );
696 * Grab the VCI number
701 * Check to see if we can process this buffer yet.
703 /* Get current DMA_Rdptr */
704 DMA_Rdptr = eup->eu_midway[MIDWAY_RX_RD];
705 /* Boundaries for first buffer */
707 start = dma_wrp >> 16;
708 stop = dma_wrp & 0xffff;
710 * Start should not equal stop because that would
711 * mean we tried inserting a NULL DMA list.
713 if ( start > stop ) { /* We wrapped */
714 if ( !(DMA_Rdptr >= stop && DMA_Rdptr < start) ) {
715 _IF_PREPEND ( &eup->eu_rxqueue, m );
719 if ( DMA_Rdptr < stop && DMA_Rdptr >= start ) {
720 _IF_PREPEND ( &eup->eu_rxqueue, m );
725 * Adapter is finished with this buffer, we can
726 * continue processing it now.
730 * Locate incoming VCC for this PDU
732 evp = (Eni_vcc *) atm_dev_vcc_find ( (Cmn_unit *)eup,
736 eup->eu_stats.eni_st_drv.drv_rv_novcc++;
743 atm_dev_pdu_print ( (Cmn_unit *)eup, (Cmn_vcc *)evp, m,
748 * Grab theoretical PDU length
753 * Quick, count the PDU
755 eup->eu_pif.pif_ipdus++;
756 eup->eu_pif.pif_ibytes += pdulen;
758 vcp = evp->ev_connvc->cvc_vcc;
761 vcp->vc_ibytes += pdulen;
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;
773 * Advance DMA write allowable pointer
775 eup->eu_rxdmawr = stop;
778 * Get packet PDU length
780 KB_PLENGET ( m, pdulen );
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.
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.
797 KB_HEADADJ ( m, -sizeof(u_long) );
798 KB_DATASTART ( m, up, u_long * );
799 *((int *)up) = (int)eni_recv_stack;
801 *((int *)up) = (int)(intptr_t)evp;
805 if (IF_HANDOFF(&atm_intrq, m, NULL)) {
808 eup->eu_stats.eni_st_drv.drv_rv_intrq++;
809 eup->eu_pif.pif_ierrors++;
812 "eni_receive_drain: ATM_INTRQ is full. Unable to pass up stack.\n" );
817 * Free zero-length buffer
824 * Look for next buffer
826 _IF_DEQUEUE ( &eup->eu_rxqueue, m );
832 * If we found any completed buffers, schedule a call into
833 * the kernel to process the atm_intrq.
836 schednetisr(NETISR_ATM);
841 * Pass incoming PDU up Stack
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.
848 * tok token to identify stack instantiation
849 * m pointer to incoming PDU buffer chain
855 eni_recv_stack ( tok, m )
859 Eni_vcc *evp = (Eni_vcc *)tok;
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.
870 "eni_recv_stack: NULL buffer, tok = %p\n", tok );
876 * Send the data up the stack
878 STACK_CALL ( CPCS_UNITDATA_SIG, evp->ev_upper,
879 (void *)evp->ev_toku, evp->ev_connvc, (intptr_t)m, 0, err );