2 * ===================================
3 * HARP | Host ATM Research Platform
4 * ===================================
6 * This Host ATM Research Platform ("HARP") file (the "Software") is
7 * made available by Network Computing Services, Inc. ("NetworkCS")
8 * "AS IS". NetworkCS does not provide maintenance, improvements or
11 * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
12 * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
14 * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
15 * In no event shall NetworkCS be responsible for any damages, including
16 * but not limited to consequential damages, arising from or relating to
17 * any use of the Software or related support.
19 * Copyright 1994-1998 Network Computing Services, Inc.
21 * Copies of this Software may be made, however, the above copyright
22 * notice must be reproduced on all copies.
25 #include <sys/cdefs.h>
26 __FBSDID("$FreeBSD$");
29 * FORE Systems 200-Series Adapter Support
30 * ---------------------------------------
32 * Receive queue management
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/socket.h>
39 #include <sys/socketvar.h>
40 #include <sys/syslog.h>
44 #include <net/netisr.h>
45 #include <netatm/port.h>
46 #include <netatm/queue.h>
47 #include <netatm/atm.h>
48 #include <netatm/atm_sys.h>
49 #include <netatm/atm_sap.h>
50 #include <netatm/atm_cm.h>
51 #include <netatm/atm_if.h>
52 #include <netatm/atm_vc.h>
53 #include <netatm/atm_stack.h>
54 #include <netatm/atm_pcb.h>
55 #include <netatm/atm_var.h>
56 #include <dev/pci/pcivar.h>
57 #include <dev/hfa/fore.h>
58 #include <dev/hfa/fore_aali.h>
59 #include <dev/hfa/fore_slave.h>
60 #include <dev/hfa/fore_stats.h>
61 #include <dev/hfa/fore_var.h>
62 #include <dev/hfa/fore_include.h>
65 __RCSID("@(#) $FreeBSD$");
72 static void fore_recv_stack(void *, KBuffer *);
76 * Allocate Receive Queue Data Structures
79 * fup pointer to device unit structure
82 * 0 allocations successful
83 * else allocation failed
86 fore_recv_allocate(fup)
93 * Allocate non-cacheable memory for receive status words
95 memp = atm_dev_alloc(sizeof(Q_status) * RECV_QUELEN,
96 QSTAT_ALIGN, ATM_DEV_NONCACHE);
100 fup->fu_recv_stat = (Q_status *) memp;
102 pmemp = vtophys(fup->fu_recv_stat);
106 fup->fu_recv_statd = pmemp;
109 * Allocate memory for receive descriptors
111 memp = atm_dev_alloc(sizeof(Recv_descr) * RECV_QUELEN,
112 RECV_DESCR_ALIGN, 0);
116 fup->fu_recv_desc = (Recv_descr *) memp;
118 pmemp = vtophys(fup->fu_recv_desc);
122 fup->fu_recv_descd = pmemp;
129 * Receive Queue Initialization
131 * Allocate and initialize the host-resident receive queue structures
132 * and then initialize the CP-resident queue structures.
134 * Called at interrupt level.
137 * fup pointer to device unit structure
143 fore_recv_initialize(fup)
146 Aali *aap = fup->fu_aali;
156 * Point to CP-resident receive queue
158 cqp = (Recv_queue *)(fup->fu_ram + CP_READ(aap->aali_recv_q));
161 * Point to host-resident receive queue structures
163 hrp = fup->fu_recv_q;
164 qsp = fup->fu_recv_stat;
165 qsp_dma = fup->fu_recv_statd;
166 rdp = fup->fu_recv_desc;
167 rdp_dma = fup->fu_recv_descd;
170 * Loop thru all queue entries and do whatever needs doing
172 for (i = 0; i < RECV_QUELEN; i++) {
175 * Set queue status word to free
180 * Set up host queue entry and link into ring
182 hrp->hrq_cpelem = cqp;
183 hrp->hrq_status = qsp;
184 hrp->hrq_descr = rdp;
185 hrp->hrq_descr_dma = rdp_dma;
186 if (i == (RECV_QUELEN - 1))
187 hrp->hrq_next = fup->fu_recv_q;
189 hrp->hrq_next = hrp + 1;
192 * Now let the CP into the game
194 cqp->cq_descr = (CP_dma) CP_WRITE(rdp_dma);
195 cqp->cq_status = (CP_dma) CP_WRITE(qsp_dma);
198 * Bump all queue pointers
202 qsp_dma += sizeof(Q_status);
204 rdp_dma += sizeof(Recv_descr);
209 * Initialize queue pointers
211 fup->fu_recv_head = fup->fu_recv_q;
218 * Drain Receive Queue
220 * This function will process all completed entries at the head of the
221 * receive queue. The received segments will be linked into a received
222 * PDU buffer chain and it will then be passed up the PDU's VCC stack for
223 * processing by the next higher protocol layer.
225 * May be called in interrupt state.
226 * Must be called with interrupts locked out.
229 * fup pointer to device unit structure
238 H_recv_queue *hrp = NULL;
244 KBuffer *m, *mhead, *mtail;
248 int i, pdulen, retries = 0, error;
250 /* Silence the compiler */
255 * Process each completed entry
258 while (*fup->fu_recv_head->hrq_status & QSTAT_COMPLETED) {
261 * Get completed entry's receive descriptor
263 hrp = fup->fu_recv_head;
264 rdp = hrp->hrq_descr;
268 * Cache flush receive descriptor
271 vac_flush((addr_t)rdp, sizeof(Recv_descr));
275 hdr = rdp->rd_cell_hdr;
276 nsegs = rdp->rd_nsegs;
283 * Locate incoming VCC for this PDU
285 fvp = (Fore_vcc *) atm_dev_vcc_find((Cmn_unit *)fup,
286 ATM_HDR_GET_VPI(hdr), ATM_HDR_GET_VCI(hdr), VCC_IN);
289 * Check for a receive error
291 * Apparently the receive descriptor itself contains valid
292 * information, but the received pdu data is probably bogus.
293 * We'll arrange for the receive buffer segments to be tossed.
295 if (*hrp->hrq_status & QSTAT_ERROR) {
297 fup->fu_pif.pif_ierrors++;
299 vcp = fvp->fv_connvc->cvc_vcc;
302 vcp->vc_nif->nif_if.if_ierrors++;
304 ATM_DEBUG1("fore receive error: hdr=0x%lx\n", hdr);
309 * Build PDU buffer chain from receive segments
311 for (i = 0, rsp = rdp->rd_seg; i < nsegs; i++, rsp++) {
313 bhp = rsp->rsd_handle;
314 seglen = rsp->rsd_len;
317 * Remove buffer from our supplied queue and get
318 * to the underlying buffer
320 switch (bhp->bh_type) {
323 DEQUEUE(bhp, Buf_handle, bh_qelem,
326 m = (KBuffer *) ((caddr_t)bhp - BUF1_SM_HOFF);
327 KB_DATASTART(m, cp, caddr_t);
331 DEQUEUE(bhp, Buf_handle, bh_qelem,
334 m = (KBuffer *) ((caddr_t)bhp - BUF1_LG_HOFF);
335 KB_DATASTART(m, cp, caddr_t);
340 "fore_recv_drain: bhp=%p type=0x%x\n",
342 panic("fore_recv_drain: bad buffer type");
346 * Toss any zero-length or receive error buffers
348 if ((seglen == 0) || error) {
354 * Link buffer into chain
357 type0 = bhp->bh_type;
358 KB_LINKHEAD(m, mhead);
368 * Flush received buffer data
374 KB_DATASTART(m, dp, addr_t);
381 * Make sure we've got a non-null PDU
388 * We only support user data PDUs (for now)
390 if (hdr & ATM_HDR_SET_PT(ATM_PT_NONUSER)) {
396 * Toss the data if there's no VCC
399 fup->fu_stats->st_drv.drv_rv_novcc++;
406 atm_dev_pdu_print((Cmn_unit *)fup, (Cmn_vcc *)fvp,
411 * Make sure we have our queueing headroom at the front
412 * of the buffer chain
414 if (type0 != BHT_S1_SMALL) {
417 * Small buffers already have headroom built-in, but
418 * if CP had to use a large buffer for the first
419 * buffer, then we have to allocate a buffer here to
420 * contain the headroom.
422 fup->fu_stats->st_drv.drv_rv_nosbf++;
424 KB_ALLOCPKT(m, BUF1_SM_SIZE, KB_F_NOWAIT, KB_T_DATA);
426 fup->fu_stats->st_drv.drv_rv_nomb++;
432 * Put new buffer at head of PDU chain
434 KB_LINKHEAD(m, mhead);
436 KB_HEADSET(m, BUF1_SM_DOFF);
441 * It looks like we've got a valid PDU - count it quick!!
443 mhead->m_pkthdr.rcvif = NULL;
444 mhead->m_pkthdr.csum_flags = 0;
445 SLIST_INIT(&mhead->m_pkthdr.tags);
446 KB_PLENSET(mhead, pdulen);
447 fup->fu_pif.pif_ipdus++;
448 fup->fu_pif.pif_ibytes += pdulen;
449 vcp = fvp->fv_connvc->cvc_vcc;
451 vcp->vc_ibytes += pdulen;
453 vcp->vc_nif->nif_ibytes += pdulen;
454 vcp->vc_nif->nif_if.if_ipackets++;
455 #if (defined(BSD) && (BSD >= 199103))
456 vcp->vc_nif->nif_if.if_ibytes += pdulen;
461 * The STACK_CALL needs to happen at splnet() in order
462 * for the stack sequence processing to work. Schedule an
463 * interrupt queue callback at splnet() since we are
464 * currently at device level.
468 * Prepend callback function pointer and token value to buffer.
469 * We have already guaranteed that the space is available
470 * in the first buffer.
471 * Don't count this extra fields in m_pkthdr.len (XXX)
473 mhead->m_data -= sizeof(atm_intr_func_t) + sizeof(void *);
474 mhead->m_len += sizeof(atm_intr_func_t) + sizeof(void *);
475 cp = mtod(mhead, caddr_t);
476 *((atm_intr_func_t *)cp) = fore_recv_stack;
477 cp += sizeof(atm_intr_func_t);
478 *((void **)cp) = (void *)fvp;
483 if (netisr_queue(NETISR_ATM, mhead)) { /* (0) on success. */
484 fup->fu_stats->st_drv.drv_rv_ifull++;
490 * Mark this entry free for use and bump head pointer
491 * to the next entry in the queue
493 *hrp->hrq_status = QSTAT_FREE;
494 hrp->hrq_cpelem->cq_descr =
495 (CP_dma) CP_WRITE((u_long)hrp->hrq_descr_dma);
496 fup->fu_recv_head = hrp->hrq_next;
500 * Nearly all of the interrupts generated by the CP will be due
501 * to PDU reception. However, we may receive an interrupt before
502 * the CP has completed the status word DMA to host memory. Thus,
503 * if we haven't processed any PDUs during this interrupt, we will
504 * wait a bit for completed work on the receive queue, rather than
505 * having to field an extra interrupt very soon.
508 if (++retries <= FORE_RECV_RETRY) {
509 DELAY(FORE_RECV_DELAY);
519 * Pass Incoming PDU up Stack
521 * This function is called via the core ATM interrupt queue callback
522 * set in fore_recv_drain(). It will pass the supplied incoming
523 * PDU up the incoming VCC's stack.
528 * tok token to identify stack instantiation
529 * m pointer to incoming PDU buffer chain
535 fore_recv_stack(tok, m)
539 Fore_vcc *fvp = (Fore_vcc *)tok;
543 * Send the data up the stack
545 STACK_CALL(CPCS_UNITDATA_SIG, fvp->fv_upper,
546 fvp->fv_toku, fvp->fv_connvc, (intptr_t)m, 0, err);
555 * Free Receive Queue Data Structures
558 * fup pointer to device unit structure
568 * We'll just let fore_buf_free() take care of freeing any
569 * buffers sitting on the receive queue (which are also still
570 * on the fu_*_bq queue).
572 if (fup->fu_flags & CUF_INITED) {
576 * Free the status words
578 if (fup->fu_recv_stat) {
579 atm_dev_free((volatile void *)fup->fu_recv_stat);
580 fup->fu_recv_stat = NULL;
581 fup->fu_recv_statd = 0;
585 * Free the receive descriptors
587 if (fup->fu_recv_desc) {
588 atm_dev_free(fup->fu_recv_desc);
589 fup->fu_recv_desc = NULL;
590 fup->fu_recv_descd = 0;