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