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.
26 * @(#) $FreeBSD: src/sys/dev/hfa/fore_receive.c,v 1.5.2.2 2003/01/23 21:06:43 sam Exp $
27 * @(#) $DragonFly: src/sys/dev/atm/hfa/fore_receive.c,v 1.7 2008/03/01 22:03:13 swildner Exp $
31 * FORE Systems 200-Series Adapter Support
32 * ---------------------------------------
34 * Receive queue management
38 #include "fore_include.h"
43 static void fore_recv_stack (void *, KBuffer *);
47 * Allocate Receive Queue Data Structures
50 * fup pointer to device unit structure
53 * 0 allocations successful
54 * else allocation failed
57 fore_recv_allocate(Fore_unit *fup)
62 * Allocate non-cacheable memory for receive status words
64 memp = atm_dev_alloc(sizeof(Q_status) * RECV_QUELEN,
65 QSTAT_ALIGN, ATM_DEV_NONCACHE);
69 fup->fu_recv_stat = (Q_status *) memp;
71 memp = DMA_GET_ADDR(fup->fu_recv_stat, sizeof(Q_status) * RECV_QUELEN,
72 QSTAT_ALIGN, ATM_DEV_NONCACHE);
76 fup->fu_recv_statd = (Q_status *) memp;
79 * Allocate memory for receive descriptors
81 memp = atm_dev_alloc(sizeof(Recv_descr) * RECV_QUELEN,
86 fup->fu_recv_desc = (Recv_descr *) memp;
88 memp = DMA_GET_ADDR(fup->fu_recv_desc,
89 sizeof(Recv_descr) * RECV_QUELEN, RECV_DESCR_ALIGN, 0);
93 fup->fu_recv_descd = (Recv_descr *) memp;
100 * Receive Queue Initialization
102 * Allocate and initialize the host-resident receive queue structures
103 * and then initialize the CP-resident queue structures.
105 * Called at interrupt level.
108 * fup pointer to device unit structure
114 fore_recv_initialize(Fore_unit *fup)
116 Aali *aap = fup->fu_aali;
126 * Point to CP-resident receive queue
128 cqp = (Recv_queue *)(fup->fu_ram + CP_READ(aap->aali_recv_q));
131 * Point to host-resident receive queue structures
133 hrp = fup->fu_recv_q;
134 qsp = fup->fu_recv_stat;
135 qsp_dma = fup->fu_recv_statd;
136 rdp = fup->fu_recv_desc;
137 rdp_dma = fup->fu_recv_descd;
140 * Loop thru all queue entries and do whatever needs doing
142 for (i = 0; i < RECV_QUELEN; i++) {
145 * Set queue status word to free
150 * Set up host queue entry and link into ring
152 hrp->hrq_cpelem = cqp;
153 hrp->hrq_status = qsp;
154 hrp->hrq_descr = rdp;
155 hrp->hrq_descr_dma = rdp_dma;
156 if (i == (RECV_QUELEN - 1))
157 hrp->hrq_next = fup->fu_recv_q;
159 hrp->hrq_next = hrp + 1;
162 * Now let the CP into the game
164 cqp->cq_descr = (CP_dma) CP_WRITE(rdp_dma);
165 cqp->cq_status = (CP_dma) CP_WRITE(qsp_dma);
168 * Bump all queue pointers
179 * Initialize queue pointers
181 fup->fu_recv_head = fup->fu_recv_q;
188 * Drain Receive Queue
190 * This function will process all completed entries at the head of the
191 * receive queue. The received segments will be linked into a received
192 * PDU buffer chain and it will then be passed up the PDU's VCC stack for
193 * processing by the next higher protocol layer.
195 * May be called in interrupt state.
196 * Must be called with interrupts locked out.
199 * fup pointer to device unit structure
205 fore_recv_drain(Fore_unit *fup)
207 H_recv_queue *hrp = NULL;
213 KBuffer *m, *mhead, *mtail;
217 int i, pdulen, retries = 0, error;
219 /* Silence the compiler */
224 * Process each completed entry
227 while (*fup->fu_recv_head->hrq_status & QSTAT_COMPLETED) {
230 * Get completed entry's receive descriptor
232 hrp = fup->fu_recv_head;
233 rdp = hrp->hrq_descr;
237 * Cache flush receive descriptor
240 vac_flush((addr_t)rdp, sizeof(Recv_descr));
244 hdr = rdp->rd_cell_hdr;
245 nsegs = rdp->rd_nsegs;
252 * Locate incoming VCC for this PDU
254 fvp = (Fore_vcc *) atm_dev_vcc_find((Cmn_unit *)fup,
255 ATM_HDR_GET_VPI(hdr), ATM_HDR_GET_VCI(hdr), VCC_IN);
258 * Check for a receive error
260 * Apparently the receive descriptor itself contains valid
261 * information, but the received pdu data is probably bogus.
262 * We'll arrange for the receive buffer segments to be tossed.
264 if (*hrp->hrq_status & QSTAT_ERROR) {
266 fup->fu_pif.pif_ierrors++;
268 vcp = fvp->fv_connvc->cvc_vcc;
271 vcp->vc_nif->nif_if.if_ierrors++;
273 ATM_DEBUG1("fore receive error: hdr=0x%lx\n", hdr);
278 * Build PDU buffer chain from receive segments
280 for (i = 0, rsp = rdp->rd_seg; i < nsegs; i++, rsp++) {
282 bhp = rsp->rsd_handle;
283 seglen = rsp->rsd_len;
286 * Remove buffer from our supplied queue and get
287 * to the underlying buffer
289 switch (bhp->bh_type) {
292 DEQUEUE(bhp, Buf_handle, bh_qelem,
295 m = (KBuffer *) ((caddr_t)bhp - BUF1_SM_HOFF);
296 KB_DATASTART(m, cp, caddr_t);
297 DMA_FREE_ADDR(cp, bhp->bh_dma, BUF1_SM_SIZE, 0);
301 DEQUEUE(bhp, Buf_handle, bh_qelem,
304 m = (KBuffer *) ((caddr_t)bhp - BUF1_LG_HOFF);
305 KB_DATASTART(m, cp, caddr_t);
306 DMA_FREE_ADDR(cp, bhp->bh_dma, BUF1_LG_SIZE, 0);
311 "fore_recv_drain: bhp=%p type=0x%x\n",
313 panic("fore_recv_drain: bad buffer type");
317 * Toss any zero-length or receive error buffers
319 if ((seglen == 0) || error) {
325 * Link buffer into chain
328 type0 = bhp->bh_type;
329 KB_LINKHEAD(m, mhead);
339 * Flush received buffer data
345 KB_DATASTART(m, dp, addr_t);
352 * Make sure we've got a non-null PDU
359 * We only support user data PDUs (for now)
361 if (hdr & ATM_HDR_SET_PT(ATM_PT_NONUSER)) {
367 * Toss the data if there's no VCC
370 fup->fu_stats->st_drv.drv_rv_novcc++;
377 atm_dev_pdu_print((Cmn_unit *)fup, (Cmn_vcc *)fvp,
382 * Make sure we have our queueing headroom at the front
383 * of the buffer chain
385 if (type0 != BHT_S1_SMALL) {
388 * Small buffers already have headroom built-in, but
389 * if CP had to use a large buffer for the first
390 * buffer, then we have to allocate a buffer here to
391 * contain the headroom.
393 fup->fu_stats->st_drv.drv_rv_nosbf++;
395 KB_ALLOCPKT(m, BUF1_SM_SIZE, KB_F_NOWAIT, KB_T_DATA);
397 fup->fu_stats->st_drv.drv_rv_nomb++;
403 * Put new buffer at head of PDU chain
405 KB_LINKHEAD(m, mhead);
407 KB_HEADSET(m, BUF1_SM_DOFF);
412 * It looks like we've got a valid PDU - count it quick!!
414 mhead->m_pkthdr.rcvif = NULL;
415 mhead->m_pkthdr.csum_flags = 0;
416 SLIST_INIT(&mhead->m_pkthdr.tags);
417 KB_PLENSET(mhead, pdulen);
418 fup->fu_pif.pif_ipdus++;
419 fup->fu_pif.pif_ibytes += pdulen;
420 vcp = fvp->fv_connvc->cvc_vcc;
422 vcp->vc_ibytes += pdulen;
424 vcp->vc_nif->nif_ibytes += pdulen;
425 vcp->vc_nif->nif_if.if_ipackets++;
426 vcp->vc_nif->nif_if.if_ibytes += pdulen;
430 * The STACK_CALL needs to happen at splnet() in order
431 * for the stack sequence processing to work. Schedule an
432 * interrupt queue callback at splnet() since we are
433 * currently at device level.
437 * Prepend callback function pointer and token value to buffer.
438 * We have already guaranteed that the space is available
439 * in the first buffer.
441 KB_HEADADJ(mhead, sizeof(atm_intr_func_t) + sizeof(int));
442 KB_DATASTART(mhead, cp, caddr_t);
443 *((atm_intr_func_t *)cp) = fore_recv_stack;
444 cp += sizeof(atm_intr_func_t);
445 *((void **)cp) = (void *)fvp;
450 if (netisr_queue(NETISR_ATM, mhead)) {
451 fup->fu_stats->st_drv.drv_rv_ifull++;
458 * Mark this entry free for use and bump head pointer
459 * to the next entry in the queue
461 *hrp->hrq_status = QSTAT_FREE;
462 hrp->hrq_cpelem->cq_descr =
463 (CP_dma) CP_WRITE((u_long)hrp->hrq_descr_dma);
464 fup->fu_recv_head = hrp->hrq_next;
468 * Nearly all of the interrupts generated by the CP will be due
469 * to PDU reception. However, we may receive an interrupt before
470 * the CP has completed the status word DMA to host memory. Thus,
471 * if we haven't processed any PDUs during this interrupt, we will
472 * wait a bit for completed work on the receive queue, rather than
473 * having to field an extra interrupt very soon.
476 if (++retries <= FORE_RECV_RETRY) {
477 DELAY(FORE_RECV_DELAY);
487 * Pass Incoming PDU up Stack
489 * This function is called via the core ATM interrupt queue callback
490 * set in fore_recv_drain(). It will pass the supplied incoming
491 * PDU up the incoming VCC's stack.
496 * tok token to identify stack instantiation
497 * m pointer to incoming PDU buffer chain
503 fore_recv_stack(void *tok, KBuffer *m)
505 Fore_vcc *fvp = (Fore_vcc *)tok;
509 * Send the data up the stack
511 STACK_CALL(CPCS_UNITDATA_SIG, fvp->fv_upper,
512 fvp->fv_toku, fvp->fv_connvc, (int)m, 0, err);
521 * Free Receive Queue Data Structures
524 * fup pointer to device unit structure
530 fore_recv_free(Fore_unit *fup)
533 * We'll just let fore_buf_free() take care of freeing any
534 * buffers sitting on the receive queue (which are also still
535 * on the fu_*_bq queue).
537 if (fup->fu_flags & CUF_INITED) {
541 * Free the status words
543 if (fup->fu_recv_stat) {
544 if (fup->fu_recv_statd) {
545 DMA_FREE_ADDR(fup->fu_recv_stat, fup->fu_recv_statd,
546 sizeof(Q_status) * RECV_QUELEN,
549 atm_dev_free((volatile void *)fup->fu_recv_stat);
550 fup->fu_recv_stat = NULL;
551 fup->fu_recv_statd = NULL;
555 * Free the receive descriptors
557 if (fup->fu_recv_desc) {
558 if (fup->fu_recv_descd) {
559 DMA_FREE_ADDR(fup->fu_recv_desc, fup->fu_recv_descd,
560 sizeof(Recv_descr) * RECV_QUELEN, 0);
562 atm_dev_free(fup->fu_recv_desc);
563 fup->fu_recv_desc = NULL;
564 fup->fu_recv_descd = NULL;