Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / dev / atm / hfa / fore_receive.c
1 /*
2  *
3  * ===================================
4  * HARP  |  Host ATM Research Platform
5  * ===================================
6  *
7  *
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.
12  *
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.
20  *
21  * Copyright 1994-1998 Network Computing Services, Inc.
22  *
23  * Copies of this Software may be made, however, the above copyright
24  * notice must be reproduced on all copies.
25  *
26  *      @(#) $FreeBSD: src/sys/dev/hfa/fore_receive.c,v 1.5.2.2 2003/01/23 21:06:43 sam Exp $
27  *
28  */
29
30 /*
31  * FORE Systems 200-Series Adapter Support
32  * ---------------------------------------
33  *
34  * Receive queue management
35  *
36  */
37
38 #include <dev/hfa/fore_include.h>
39
40 #ifndef lint
41 __RCSID("@(#) $FreeBSD: src/sys/dev/hfa/fore_receive.c,v 1.5.2.2 2003/01/23 21:06:43 sam Exp $");
42 #endif
43
44
45 /*
46  * Local functions
47  */
48 static void     fore_recv_stack __P((void *, KBuffer *));
49
50
51 /*
52  * Allocate Receive Queue Data Structures
53  *
54  * Arguments:
55  *      fup             pointer to device unit structure
56  *
57  * Returns:
58  *      0               allocations successful
59  *      else            allocation failed
60  */
61 int
62 fore_recv_allocate(fup)
63         Fore_unit       *fup;
64 {
65         caddr_t         memp;
66
67         /*
68          * Allocate non-cacheable memory for receive status words
69          */
70         memp = atm_dev_alloc(sizeof(Q_status) * RECV_QUELEN,
71                         QSTAT_ALIGN, ATM_DEV_NONCACHE);
72         if (memp == NULL) {
73                 return (1);
74         }
75         fup->fu_recv_stat = (Q_status *) memp;
76
77         memp = DMA_GET_ADDR(fup->fu_recv_stat, sizeof(Q_status) * RECV_QUELEN,
78                         QSTAT_ALIGN, ATM_DEV_NONCACHE);
79         if (memp == NULL) {
80                 return (1);
81         }
82         fup->fu_recv_statd = (Q_status *) memp;
83
84         /*
85          * Allocate memory for receive descriptors
86          */
87         memp = atm_dev_alloc(sizeof(Recv_descr) * RECV_QUELEN,
88                         RECV_DESCR_ALIGN, 0);
89         if (memp == NULL) {
90                 return (1);
91         }
92         fup->fu_recv_desc = (Recv_descr *) memp;
93
94         memp = DMA_GET_ADDR(fup->fu_recv_desc,
95                         sizeof(Recv_descr) * RECV_QUELEN, RECV_DESCR_ALIGN, 0);
96         if (memp == NULL) {
97                 return (1);
98         }
99         fup->fu_recv_descd = (Recv_descr *) memp;
100
101         return (0);
102 }
103
104
105 /*
106  * Receive Queue Initialization
107  *
108  * Allocate and initialize the host-resident receive queue structures
109  * and then initialize the CP-resident queue structures.
110  * 
111  * Called at interrupt level.
112  *
113  * Arguments:
114  *      fup             pointer to device unit structure
115  *
116  * Returns:
117  *      none
118  */
119 void
120 fore_recv_initialize(fup)
121         Fore_unit       *fup;
122 {
123         Aali            *aap = fup->fu_aali;
124         Recv_queue      *cqp;
125         H_recv_queue    *hrp;
126         Recv_descr      *rdp;
127         Recv_descr      *rdp_dma;
128         Q_status        *qsp;
129         Q_status        *qsp_dma;
130         int             i;
131
132         /*
133          * Point to CP-resident receive queue
134          */
135         cqp = (Recv_queue *)(fup->fu_ram + CP_READ(aap->aali_recv_q));
136
137         /*
138          * Point to host-resident receive queue structures
139          */
140         hrp = fup->fu_recv_q;
141         qsp = fup->fu_recv_stat;
142         qsp_dma = fup->fu_recv_statd;
143         rdp = fup->fu_recv_desc;
144         rdp_dma = fup->fu_recv_descd;
145
146         /*
147          * Loop thru all queue entries and do whatever needs doing
148          */
149         for (i = 0; i < RECV_QUELEN; i++) {
150
151                 /*
152                  * Set queue status word to free
153                  */
154                 *qsp = QSTAT_FREE;
155
156                 /*
157                  * Set up host queue entry and link into ring
158                  */
159                 hrp->hrq_cpelem = cqp;
160                 hrp->hrq_status = qsp;
161                 hrp->hrq_descr = rdp;
162                 hrp->hrq_descr_dma = rdp_dma;
163                 if (i == (RECV_QUELEN - 1))
164                         hrp->hrq_next = fup->fu_recv_q;
165                 else
166                         hrp->hrq_next = hrp + 1;
167
168                 /*
169                  * Now let the CP into the game
170                  */
171                 cqp->cq_descr = (CP_dma) CP_WRITE(rdp_dma);
172                 cqp->cq_status = (CP_dma) CP_WRITE(qsp_dma);
173
174                 /*
175                  * Bump all queue pointers
176                  */
177                 hrp++;
178                 qsp++;
179                 qsp_dma++;
180                 rdp++;
181                 rdp_dma++;
182                 cqp++;
183         }
184
185         /*
186          * Initialize queue pointers
187          */
188         fup->fu_recv_head = fup->fu_recv_q;
189
190         return;
191 }
192
193
194 /*
195  * Drain Receive Queue
196  *
197  * This function will process all completed entries at the head of the
198  * receive queue.  The received segments will be linked into a received
199  * PDU buffer chain and it will then be passed up the PDU's VCC stack for 
200  * processing by the next higher protocol layer.
201  *
202  * May be called in interrupt state.
203  * Must be called with interrupts locked out.
204  *
205  * Arguments:
206  *      fup             pointer to device unit structure
207  *
208  * Returns:
209  *      none
210  */
211 void
212 fore_recv_drain(fup)
213         Fore_unit       *fup;
214 {
215         H_recv_queue    *hrp = NULL;
216         Recv_descr      *rdp;
217         Recv_seg_descr  *rsp;
218         Buf_handle      *bhp;
219         Fore_vcc        *fvp;
220         struct vccb     *vcp;
221         KBuffer         *m, *mhead, *mtail;
222         caddr_t         cp;
223         u_long          hdr, nsegs;
224         u_int           seglen, type0;
225         int             i, pdulen, retries = 0, error;
226
227         /* Silence the compiler */
228         mtail = NULL;
229         type0 = 0;
230
231         /*
232          * Process each completed entry
233          */
234 retry:
235         while (*fup->fu_recv_head->hrq_status & QSTAT_COMPLETED) {
236
237                 /*
238                  * Get completed entry's receive descriptor
239                  */
240                 hrp = fup->fu_recv_head;
241                 rdp = hrp->hrq_descr;
242
243 #ifdef VAC
244                 /*
245                  * Cache flush receive descriptor 
246                  */
247                 if (vac) {
248                         vac_flush((addr_t)rdp, sizeof(Recv_descr));
249                 }
250 #endif
251
252                 hdr = rdp->rd_cell_hdr;
253                 nsegs = rdp->rd_nsegs;
254
255                 pdulen = 0;
256                 error = 0;
257                 mhead = NULL;
258
259                 /*
260                  * Locate incoming VCC for this PDU
261                  */
262                 fvp = (Fore_vcc *) atm_dev_vcc_find((Cmn_unit *)fup,
263                         ATM_HDR_GET_VPI(hdr), ATM_HDR_GET_VCI(hdr), VCC_IN);
264
265                 /*
266                  * Check for a receive error
267                  *
268                  * Apparently the receive descriptor itself contains valid 
269                  * information, but the received pdu data is probably bogus.
270                  * We'll arrange for the receive buffer segments to be tossed.
271                  */
272                 if (*hrp->hrq_status & QSTAT_ERROR) {
273
274                         fup->fu_pif.pif_ierrors++;
275                         if (fvp) {
276                                 vcp = fvp->fv_connvc->cvc_vcc;
277                                 vcp->vc_ierrors++;
278                                 if (vcp->vc_nif)
279                                         vcp->vc_nif->nif_if.if_ierrors++;
280                         }
281                         ATM_DEBUG1("fore receive error: hdr=0x%lx\n", hdr);
282                         error = 1;
283                 }
284
285                 /*
286                  * Build PDU buffer chain from receive segments
287                  */
288                 for (i = 0, rsp = rdp->rd_seg; i < nsegs; i++, rsp++) {
289
290                         bhp = rsp->rsd_handle;
291                         seglen = rsp->rsd_len;
292
293                         /*
294                          * Remove buffer from our supplied queue and get
295                          * to the underlying buffer
296                          */
297                         switch (bhp->bh_type) {
298
299                         case BHT_S1_SMALL:
300                                 DEQUEUE(bhp, Buf_handle, bh_qelem,
301                                         fup->fu_buf1s_bq);
302                                 fup->fu_buf1s_cnt--;
303                                 m = (KBuffer *) ((caddr_t)bhp - BUF1_SM_HOFF);
304                                 KB_DATASTART(m, cp, caddr_t);
305                                 DMA_FREE_ADDR(cp, bhp->bh_dma, BUF1_SM_SIZE, 0);
306                                 break;
307
308                         case BHT_S1_LARGE:
309                                 DEQUEUE(bhp, Buf_handle, bh_qelem,
310                                         fup->fu_buf1l_bq);
311                                 fup->fu_buf1l_cnt--;
312                                 m = (KBuffer *) ((caddr_t)bhp - BUF1_LG_HOFF);
313                                 KB_DATASTART(m, cp, caddr_t);
314                                 DMA_FREE_ADDR(cp, bhp->bh_dma, BUF1_LG_SIZE, 0);
315                                 break;
316
317                         default:
318                                 log(LOG_ERR,
319                                         "fore_recv_drain: bhp=%p type=0x%x\n",
320                                         bhp, bhp->bh_type);
321                                 panic("fore_recv_drain: bad buffer type");
322                         }
323
324                         /*
325                          * Toss any zero-length or receive error buffers 
326                          */
327                         if ((seglen == 0) || error) {
328                                 KB_FREEALL(m);
329                                 continue;
330                         }
331
332                         /*
333                          * Link buffer into chain
334                          */
335                         if (mhead == NULL) {
336                                 type0 = bhp->bh_type;
337                                 KB_LINKHEAD(m, mhead);
338                                 mhead = m;
339                         } else {
340                                 KB_LINK(m, mtail);
341                         }
342                         KB_LEN(m) = seglen;
343                         pdulen += seglen;
344                         mtail = m;
345
346                         /*
347                          * Flush received buffer data
348                          */
349 #ifdef VAC
350                         if (vac) {
351                                 addr_t  dp;
352
353                                 KB_DATASTART(m, dp, addr_t);
354                                 vac_pageflush(dp);
355                         }
356 #endif
357                 }
358
359                 /*
360                  * Make sure we've got a non-null PDU
361                  */
362                 if (mhead == NULL) {
363                         goto free_ent;
364                 }
365
366                 /*
367                  * We only support user data PDUs (for now)
368                  */
369                 if (hdr & ATM_HDR_SET_PT(ATM_PT_NONUSER)) {
370                         KB_FREEALL(mhead);
371                         goto free_ent;
372                 }
373
374                 /*
375                  * Toss the data if there's no VCC
376                  */
377                 if (fvp == NULL) {
378                         fup->fu_stats->st_drv.drv_rv_novcc++;
379                         KB_FREEALL(mhead);
380                         goto free_ent;
381                 }
382
383 #ifdef DIAGNOSTIC
384                 if (atm_dev_print)
385                         atm_dev_pdu_print((Cmn_unit *)fup, (Cmn_vcc *)fvp, 
386                                 mhead, "fore_recv");
387 #endif
388
389                 /*
390                  * Make sure we have our queueing headroom at the front
391                  * of the buffer chain
392                  */
393                 if (type0 != BHT_S1_SMALL) {
394
395                         /*
396                          * Small buffers already have headroom built-in, but
397                          * if CP had to use a large buffer for the first 
398                          * buffer, then we have to allocate a buffer here to
399                          * contain the headroom.
400                          */
401                         fup->fu_stats->st_drv.drv_rv_nosbf++;
402
403                         KB_ALLOCPKT(m, BUF1_SM_SIZE, KB_F_NOWAIT, KB_T_DATA);
404                         if (m == NULL) {
405                                 fup->fu_stats->st_drv.drv_rv_nomb++;
406                                 KB_FREEALL(mhead);
407                                 goto free_ent;
408                         }
409
410                         /*
411                          * Put new buffer at head of PDU chain
412                          */
413                         KB_LINKHEAD(m, mhead);
414                         KB_LEN(m) = 0;
415                         KB_HEADSET(m, BUF1_SM_DOFF);
416                         mhead = m;
417                 }
418
419                 /*
420                  * It looks like we've got a valid PDU - count it quick!!
421                  */
422                 mhead->m_pkthdr.rcvif = NULL;
423                 mhead->m_pkthdr.csum_flags = 0;
424                 SLIST_INIT(&mhead->m_pkthdr.tags);
425                 KB_PLENSET(mhead, pdulen);
426                 fup->fu_pif.pif_ipdus++;
427                 fup->fu_pif.pif_ibytes += pdulen;
428                 vcp = fvp->fv_connvc->cvc_vcc;
429                 vcp->vc_ipdus++;
430                 vcp->vc_ibytes += pdulen;
431                 if (vcp->vc_nif) {
432                         vcp->vc_nif->nif_ibytes += pdulen;
433                         vcp->vc_nif->nif_if.if_ipackets++;
434 #if (defined(BSD) && (BSD >= 199103))
435                         vcp->vc_nif->nif_if.if_ibytes += pdulen;
436 #endif
437                 }
438
439                 /*
440                  * The STACK_CALL needs to happen at splnet() in order
441                  * for the stack sequence processing to work.  Schedule an
442                  * interrupt queue callback at splnet() since we are 
443                  * currently at device level.
444                  */
445
446                 /*
447                  * Prepend callback function pointer and token value to buffer.
448                  * We have already guaranteed that the space is available
449                  * in the first buffer.
450                  */
451                 KB_HEADADJ(mhead, sizeof(atm_intr_func_t) + sizeof(int));
452                 KB_DATASTART(mhead, cp, caddr_t);
453                 *((atm_intr_func_t *)cp) = fore_recv_stack;
454                 cp += sizeof(atm_intr_func_t);
455                 *((void **)cp) = (void *)fvp;
456
457                 /*
458                  * Schedule callback
459                  */
460                 if (!IF_QFULL(&atm_intrq)) {
461                         IF_ENQUEUE(&atm_intrq, mhead);
462                         SCHED_ATM;
463                 } else {
464                         fup->fu_stats->st_drv.drv_rv_ifull++;
465                         KB_FREEALL(mhead);
466                         goto free_ent;
467                 }
468
469 free_ent:
470                 /*
471                  * Mark this entry free for use and bump head pointer
472                  * to the next entry in the queue
473                  */
474                 *hrp->hrq_status = QSTAT_FREE;
475                 hrp->hrq_cpelem->cq_descr = 
476                         (CP_dma) CP_WRITE((u_long)hrp->hrq_descr_dma);
477                 fup->fu_recv_head = hrp->hrq_next;
478         }
479
480         /*
481          * Nearly all of the interrupts generated by the CP will be due
482          * to PDU reception.  However, we may receive an interrupt before
483          * the CP has completed the status word DMA to host memory.  Thus,
484          * if we haven't processed any PDUs during this interrupt, we will
485          * wait a bit for completed work on the receive queue, rather than 
486          * having to field an extra interrupt very soon.
487          */
488         if (hrp == NULL) {
489                 if (++retries <= FORE_RECV_RETRY) {
490                         DELAY(FORE_RECV_DELAY);
491                         goto retry;
492                 }
493         }
494
495         return;
496 }
497
498
499 /*
500  * Pass Incoming PDU up Stack
501  *
502  * This function is called via the core ATM interrupt queue callback 
503  * set in fore_recv_drain().  It will pass the supplied incoming 
504  * PDU up the incoming VCC's stack.
505  *
506  * Called at splnet.
507  *
508  * Arguments:
509  *      tok             token to identify stack instantiation
510  *      m               pointer to incoming PDU buffer chain
511  *
512  * Returns:
513  *      none
514  */
515 static void
516 fore_recv_stack(tok, m)
517         void            *tok;
518         KBuffer         *m;
519 {
520         Fore_vcc        *fvp = (Fore_vcc *)tok;
521         int             err;
522
523         /*
524          * Send the data up the stack
525          */
526         STACK_CALL(CPCS_UNITDATA_SIG, fvp->fv_upper,
527                 fvp->fv_toku, fvp->fv_connvc, (int)m, 0, err);
528         if (err)
529                 KB_FREEALL(m);
530
531         return;
532 }
533
534
535 /*
536  * Free Receive Queue Data Structures
537  *
538  * Arguments:
539  *      fup             pointer to device unit structure
540  *
541  * Returns:
542  *      none
543  */
544 void
545 fore_recv_free(fup)
546         Fore_unit       *fup;
547 {
548         /*
549          * We'll just let fore_buf_free() take care of freeing any
550          * buffers sitting on the receive queue (which are also still
551          * on the fu_*_bq queue).
552          */
553         if (fup->fu_flags & CUF_INITED) {
554         }
555
556         /*
557          * Free the status words
558          */
559         if (fup->fu_recv_stat) {
560                 if (fup->fu_recv_statd) {
561                         DMA_FREE_ADDR(fup->fu_recv_stat, fup->fu_recv_statd,
562                                 sizeof(Q_status) * RECV_QUELEN,
563                                 ATM_DEV_NONCACHE);
564                 }
565                 atm_dev_free((volatile void *)fup->fu_recv_stat);
566                 fup->fu_recv_stat = NULL;
567                 fup->fu_recv_statd = NULL;
568         }
569
570         /*
571          * Free the receive descriptors
572          */
573         if (fup->fu_recv_desc) {
574                 if (fup->fu_recv_descd) {
575                         DMA_FREE_ADDR(fup->fu_recv_desc, fup->fu_recv_descd,
576                                 sizeof(Recv_descr) * RECV_QUELEN, 0);
577                 }
578                 atm_dev_free(fup->fu_recv_desc);
579                 fup->fu_recv_desc = NULL;
580                 fup->fu_recv_descd = NULL;
581         }
582
583         return;
584 }
585