include upstream ip1000a driver version 2.09f
[linux-2.4.git] / drivers / isdn / eicon / idi.c
1 /*
2  * Core driver for Diva Server cards
3  * Implements the IDI interface
4  *
5  * Copyright (C) Eicon Technology Corporation, 2000.
6  *
7  * Eicon File Revision :    1.8  
8  *
9  * This software may be used and distributed according to the terms
10  * of the GNU General Public License, incorporated herein by reference.
11  *
12  */
13
14 #include "idi.h"
15 #include "adapter.h"
16 #include "pc.h"
17 #include "pr_pc.h"
18 #include "sys.h"
19 #include "uxio.h"
20
21 /* IDI request functions */
22
23 static void     request(card_t *card, ENTITY *e);
24
25 static void req_0(ENTITY *e)    { request(&DivasCards[ 0], e); }
26 static void req_1(ENTITY *e)    { request(&DivasCards[ 1], e); }
27 static void req_2(ENTITY *e)    { request(&DivasCards[ 2], e); }
28 static void req_3(ENTITY *e)    { request(&DivasCards[ 3], e); }
29 static void req_4(ENTITY *e)    { request(&DivasCards[ 4], e); }
30 static void req_5(ENTITY *e)    { request(&DivasCards[ 5], e); }
31 static void req_6(ENTITY *e)    { request(&DivasCards[ 6], e); }
32 static void req_7(ENTITY *e)    { request(&DivasCards[ 7], e); }
33 static void req_8(ENTITY *e)    { request(&DivasCards[ 8], e); }
34 static void req_9(ENTITY *e)    { request(&DivasCards[ 9], e); }
35 static void req_10(ENTITY *e)   { request(&DivasCards[10], e); }
36 static void req_11(ENTITY *e)   { request(&DivasCards[11], e); }
37 static void req_12(ENTITY *e)   { request(&DivasCards[12], e); }
38 static void req_13(ENTITY *e)   { request(&DivasCards[13], e); }
39 static void req_14(ENTITY *e)   { request(&DivasCards[14], e); }
40 static void req_15(ENTITY *e)   { request(&DivasCards[15], e); }
41
42 IDI_CALL DivasIdiRequest[16] =
43 {
44     &req_0,             &req_1,         &req_2,         &req_3,
45     &req_4,             &req_5,         &req_6,         &req_7,
46     &req_8,             &req_9,         &req_10,        &req_11,
47         &req_12,        &req_13,        &req_14,        &req_15
48 };
49
50 #define PR_RAM  ((struct pr_ram *)0)
51 #define RAM ((struct dual *)0)
52
53 /*------------------------------------------------------------------*/
54 /* local function prototypes                                        */
55 /*------------------------------------------------------------------*/
56
57 static byte isdn_rc(ADAPTER *, byte, byte, byte, word);
58 static byte isdn_ind(ADAPTER *, byte, byte, byte, PBUFFER *, byte, word);
59
60 /*
61  * IDI related functions
62  */
63
64 static
65 ENTITY  *entity_ptr(ADAPTER *a, byte e_no)
66 {
67         card_t  *card;
68
69         card = a->io;
70
71         return card->e_tbl[e_no].e;
72 }
73
74 static
75 void    CALLBACK(ADAPTER *a, ENTITY *e)
76 {
77         card_t  *card = a->io;
78
79         if (card->log_types & DIVAS_LOG_IDI)
80         {
81                 DivasLogIdi(card, e, FALSE);
82         }
83
84         (*e->callback)(e);
85 }
86
87 static
88 void    *PTR_P(ADAPTER *a, ENTITY *e, void *P)
89 {
90         return(P);
91 }
92
93 static
94 void    *PTR_R(ADAPTER *a, ENTITY *e)
95 {
96         return((void*)e->R);
97 }
98
99 static
100 void    *PTR_X(ADAPTER *a, ENTITY *e)
101 {
102         return((void*)e->X);
103 }
104
105 static
106 void    free_entity(ADAPTER *a, byte e_no) 
107 {
108         card_t  *card;
109         int             ipl;
110
111         card = a->io;
112
113         ipl = UxCardLock(card->hw);
114
115         card->e_tbl[e_no].e = NULL;
116         card->e_count--;
117
118         UxCardUnlock(card->hw, ipl);
119
120         return;
121 }
122
123 static
124 void    assign_queue(ADAPTER * a, byte e_no, word ref)
125 {
126         card_t  *card;
127         int             ipl;
128
129         card = a->io;
130
131         ipl = UxCardLock(card->hw);
132
133         card->e_tbl[e_no].assign_ref = ref;
134         card->e_tbl[e_no].next = card->assign;
135         card->assign = e_no;
136
137         UxCardUnlock(card->hw, ipl);
138
139         return;
140 }
141
142 static
143 byte    get_assign(ADAPTER *a, word ref)
144 {
145         card_t  *card;
146         byte    e_no;
147         int             ipl;
148
149         card = a->io;
150
151         ipl = UxCardLock(card->hw);
152
153         e_no = (byte)card->assign;
154         while (e_no)
155         {
156                 if (card->e_tbl[e_no].assign_ref == ref)
157                 {
158                         break;
159                 }
160                 e_no = card->e_tbl[e_no].next;
161         }
162
163         UxCardUnlock(card->hw, ipl);
164
165         return e_no;
166 }
167
168 static
169 void    req_queue(ADAPTER * a, byte e_no)
170 {
171         card_t  *card;
172         int             ipl;
173
174         card = a->io;
175
176         ipl = UxCardLock(card->hw);
177
178         card->e_tbl[e_no].next = 0;
179
180         if (card->e_head)
181         {
182                 card->e_tbl[card->e_tail].next = e_no;
183                 card->e_tail = e_no;
184         }
185         else
186         {
187                 card->e_head = e_no;
188                 card->e_tail = e_no;
189         }
190
191         UxCardUnlock(card->hw, ipl);
192
193         return;
194 }
195
196 static
197 byte    look_req(ADAPTER * a)
198 {
199         card_t  *card;
200
201         card = a->io;
202
203         return(card->e_head);
204 }
205
206 static
207 void    next_req(ADAPTER * a)
208 {
209         card_t  *card;
210         int             ipl;
211
212
213         card = a->io;
214
215         ipl = UxCardLock(card->hw);
216
217         card->e_head = card->e_tbl[card->e_head].next;
218         if (!card->e_head)
219         {
220                 card->e_tail = 0;
221         }
222
223         UxCardUnlock(card->hw, ipl);
224
225         return;
226 }
227
228
229 /*
230  * IDI request function for active cards
231  */
232 static
233 void    request(card_t *card, ENTITY *e)
234 {
235         word    *special_req;
236         int             i;
237         int             ipl;
238
239
240         if (card->log_types & DIVAS_LOG_IDI)
241         {
242                 DivasLogIdi(card, e, TRUE);
243         }
244
245         if (!e->Req)
246         {
247                 special_req = (word *) e;
248
249                 switch (*special_req)
250                 {
251                 case REQ_REMOVE:
252                         return;
253
254                 case REQ_NAME:
255                         for (i=0; i < DIM(card->cfg.name); i++)
256                         {
257                                 ((struct get_name_s *) e)->name[i] = card->cfg.name[i];
258                         }
259                         return;
260
261                 case REQ_SERIAL:
262                 case REQ_XLOG:
263                         DPRINTF(("IDI: attempted REQ_SERIAL or REQ_XLOG"));
264                         return;
265
266                 default:
267                         return;
268                 }
269         }
270
271         ipl = UxCardLock(card->hw);
272
273         if (!(e->Id & 0x1f))
274         {
275                 DPRINTF(("IDI: ASSIGN req"));
276
277                 for (i = 1; i < card->e_max; i++)
278                 {
279                         if (!card->e_tbl[i].e)
280                         {
281                                 break;
282                         }
283                 }
284
285                 if (i == card->e_max)
286                 {
287                         DPRINTF(("IDI: request all ids in use (IDI req ignored)"));
288                         UxCardUnlock(card->hw, ipl);
289                         e->Rc = OUT_OF_RESOURCES;
290                         return;
291                 }
292
293                 card->e_tbl[i].e = e;
294                 card->e_count++;
295
296                 e->No = (byte) i;
297                 e->More = 0;
298                 e->RCurrent = 0xff;
299         }
300         else
301         {
302                 i = e->No;
303         }
304     
305     if (e->More & XBUSY)
306         {
307                 DPRINTF(("IDI: request - entity is busy"));
308                 UxCardUnlock(card->hw, ipl);
309                 return;
310         }
311
312         e->More |= XBUSY;
313         e->More &= ~ XMOREF;
314         e->XCurrent = 0;
315         e->XOffset = 0;
316
317         card->e_tbl[i].next = 0;
318
319         if(card->e_head)
320         {
321                 card->e_tbl[card->e_tail].next = i;
322                 card->e_tail = i;
323         }
324         else
325         {
326                 card->e_head = i;
327                 card->e_tail = i;
328         }
329
330         UxCardUnlock(card->hw, ipl);
331
332         DivasScheduleRequestDpc();
333
334         return;
335 }
336
337 static byte pr_ready(ADAPTER * a)
338 {
339   byte ReadyCount;
340
341   ReadyCount = (byte)(a->ram_in(a, &PR_RAM->ReqOutput) -
342                       a->ram_in(a, &PR_RAM->ReqInput));
343
344   if(!ReadyCount) {
345     if(!a->ReadyInt) {
346       a->ram_inc(a, &PR_RAM->ReadyInt);
347       a->ReadyInt++;
348     }
349   }
350   return ReadyCount;
351 }
352
353 /*------------------------------------------------------------------*/
354 /* output function                                                  */
355 /*------------------------------------------------------------------*/
356
357 void DivasOut(ADAPTER * a)
358 {
359   byte e_no;
360   ENTITY  * this = NULL;
361   BUFFERS  *X;
362   word length;
363   word i;
364   word clength;
365   REQ * ReqOut;
366   byte more;
367   byte ReadyCount;
368   byte ReqCount;
369   byte Id;
370
371         /* while a request is pending ...                           */
372   e_no = look_req(a);
373   if(!e_no)
374   {
375     return;
376   }
377
378   ReadyCount = pr_ready(a);
379   if(!ReadyCount)
380   {
381     DPRINTF(("IDI: card not ready for next request"));
382     return;
383   }
384
385   ReqCount = 0;
386   while(e_no && ReadyCount) {
387
388     next_req(a);
389
390     this = entity_ptr(a, e_no);
391
392 #ifdef  USE_EXTENDED_DEBUGS
393         if ( !this )
394         {
395                 ISDN_ADAPTER *io = (ISDN_ADAPTER *)a->io ;
396                 DBG_FTL(("!A%d ==> NULL entity ptr - try to ignore", (int)io->ANum))
397                 e_no = look_req(a) ;
398                 ReadyCount-- ;
399                 continue ;
400         }
401         {
402                 ISDN_ADAPTER *io = (ISDN_ADAPTER *)a->io ;
403                 DPRINTF(("IDI: >A%d Id=0x%x Req=0x%x", io->ANum, this->Id, this->Req))
404         }
405 #else
406     DPRINTF(("IDI: >REQ=%x,Id=%x,Ch=%x",this->Req,this->Id,this->ReqCh));
407 #endif
408
409         /* get address of next available request buffer             */
410     ReqOut = (REQ *)&PR_RAM->B[a->ram_inw(a, &PR_RAM->NextReq)];
411
412         /* now copy the data from the current data buffer into the  */
413         /* adapters request buffer                                  */
414     length = 0;
415     i = this->XCurrent;
416     X = PTR_X(a,this);
417     while(i<this->XNum && length<270) {
418       clength = (word)(270-length);
419       if (clength > X[i].PLength-this->XOffset)
420               clength = X[i].PLength-this->XOffset;
421       a->ram_out_buffer(a,
422                         &ReqOut->XBuffer.P[length],
423                         PTR_P(a,this,&X[i].P[this->XOffset]),
424                         clength);
425
426       length +=clength;
427       this->XOffset +=clength;
428       if(this->XOffset==X[i].PLength) {
429         this->XCurrent = (byte)++i;
430         this->XOffset = 0;
431       }
432     }
433
434     a->ram_outw(a, &ReqOut->XBuffer.length, length);
435     a->ram_out(a, &ReqOut->ReqId, this->Id);
436     a->ram_out(a, &ReqOut->ReqCh, this->ReqCh);
437
438         /* if its a specific request (no ASSIGN) ...                */
439
440     if(this->Id &0x1f) {
441
442         /* if buffers are left in the list of data buffers do       */
443         /* do chaining (LL_MDATA, N_MDATA)                          */
444
445       this->More++;
446       if(i<this->XNum && this->MInd) {
447         a->ram_out(a, &ReqOut->Req, this->MInd);
448         more = TRUE;
449       }
450       else {
451         this->More |=XMOREF;
452         a->ram_out(a, &ReqOut->Req, this->Req);
453         more = FALSE;
454       }
455
456         /* if we did chaining, this entity is put back into the     */
457         /* request queue                                            */
458
459       if(more) {
460         req_queue(a,this->No);
461       }
462     }
463
464         /* else it's a ASSIGN                                       */
465
466     else {
467
468         /* save the request code used for buffer chaining           */
469
470       this->MInd = 0;
471       if (this->Id==BLLC_ID) this->MInd = LL_MDATA;
472       if (this->Id==NL_ID   ||
473           this->Id==TASK_ID ||
474           this->Id==MAN_ID
475         ) this->MInd = N_MDATA;
476
477         /* send the ASSIGN                                          */
478
479       this->More |=XMOREF;
480       a->ram_out(a, &ReqOut->Req, this->Req);
481
482         /* save the reference of the ASSIGN                         */
483
484       assign_queue(a, this->No, a->ram_inw(a, &ReqOut->Reference));
485     }
486     a->ram_outw(a, &PR_RAM->NextReq, a->ram_inw(a, &ReqOut->next));
487     ReadyCount--;
488     ReqCount++;
489
490     e_no = look_req(a);
491   }
492
493         /* send the filled request buffers to the ISDN adapter      */
494
495   a->ram_out(a, &PR_RAM->ReqInput,
496              (byte)(a->ram_in(a, &PR_RAM->ReqInput) + ReqCount));
497
498         /* if it is a 'unreturncoded' UREMOVE request, remove the  */
499         /* Id from our table after sending the request             */
500   if(this->Req==UREMOVE && this->Id) {
501     Id = this->Id;
502     e_no = a->IdTable[Id];
503     free_entity(a, e_no);
504     a->IdTable[Id] = 0;
505     this->Id = 0;
506   }
507
508 }
509
510 /*------------------------------------------------------------------*/
511 /* isdn interrupt handler                                           */
512 /*------------------------------------------------------------------*/
513
514 byte DivasDpc(ADAPTER * a)
515 {
516   byte Count;
517   RC * RcIn;
518   IND * IndIn;
519   byte c;
520   byte RNRId;
521   byte Rc;
522   byte Ind;
523
524         /* if return codes are available ...                        */
525   if((Count = a->ram_in(a, &PR_RAM->RcOutput))) {
526
527     DPRINTF(("IDI: #Rc=%x",Count));
528
529         /* get the buffer address of the first return code          */
530     RcIn = (RC *)&PR_RAM->B[a->ram_inw(a, &PR_RAM->NextRc)];
531
532         /* for all return codes do ...                              */
533     while(Count--) {
534
535       if((Rc=a->ram_in(a, &RcIn->Rc))) {
536
537         /* call return code handler, if it is not our return code   */
538         /* the handler returns 2                                    */
539         /* for all return codes we process, we clear the Rc field   */
540         isdn_rc(a,
541                 Rc,
542                 a->ram_in(a, &RcIn->RcId),
543                 a->ram_in(a, &RcIn->RcCh),
544                 a->ram_inw(a, &RcIn->Reference));
545
546         a->ram_out(a, &RcIn->Rc, 0);
547       }
548
549         /* get buffer address of next return code                   */
550       RcIn = (RC *)&PR_RAM->B[a->ram_inw(a, &RcIn->next)];
551     }
552
553         /* clear all return codes (no chaining!)                    */
554     a->ram_out(a, &PR_RAM->RcOutput ,0);
555
556         /* call output function                                     */
557     DivasOut(a);
558   }
559
560         /* clear RNR flag                                           */
561   RNRId = 0;
562
563         /* if indications are available ...                         */
564   if((Count = a->ram_in(a, &PR_RAM->IndOutput))) {
565
566     DPRINTF(("IDI: #Ind=%x",Count));
567
568         /* get the buffer address of the first indication           */
569     IndIn = (IND *)&PR_RAM->B[a->ram_inw(a, &PR_RAM->NextInd)];
570
571         /* for all indications do ...                               */
572     while(Count--) {
573
574         /* if the application marks an indication as RNR, all       */
575         /* indications from the same Id delivered in this interrupt */
576         /* are marked RNR                                           */
577       if(RNRId && RNRId==a->ram_in(a, &IndIn->IndId)) {
578         a->ram_out(a, &IndIn->Ind, 0);
579         a->ram_out(a, &IndIn->RNR, TRUE);
580       }
581       else {
582         Ind = a->ram_in(a, &IndIn->Ind);
583         if(Ind) {
584           RNRId = 0;
585
586         /* call indication handler, a return value of 2 means chain */
587         /* a return value of 1 means RNR                            */
588         /* for all indications we process, we clear the Ind field   */
589           c = isdn_ind(a,
590                        Ind,
591                        a->ram_in(a, &IndIn->IndId),
592                        a->ram_in(a, &IndIn->IndCh),
593                        &IndIn->RBuffer,
594                        a->ram_in(a, &IndIn->MInd),
595                        a->ram_inw(a, &IndIn->MLength));
596
597           if(c==1) {
598             DPRINTF(("IDI: RNR"));
599             a->ram_out(a, &IndIn->Ind, 0);
600             RNRId = a->ram_in(a, &IndIn->IndId);
601             a->ram_out(a, &IndIn->RNR, TRUE);
602           }
603         }
604       }
605
606         /* get buffer address of next indication                    */
607       IndIn = (IND *)&PR_RAM->B[a->ram_inw(a, &IndIn->next)];
608     }
609
610     a->ram_out(a, &PR_RAM->IndOutput, 0);
611   }
612   return FALSE;
613 }
614
615 byte DivasTestInt(ADAPTER * a)
616 {
617   return a->ram_in(a,(void *)0x3fe);
618 }
619
620 void DivasClearInt(ADAPTER * a)
621 {
622   a->ram_out(a,(void *)0x3fe,0); 
623 }
624
625 /*------------------------------------------------------------------*/
626 /* return code handler                                              */
627 /*------------------------------------------------------------------*/
628
629 static
630 byte isdn_rc(ADAPTER * a,
631              byte Rc,
632              byte Id,
633              byte Ch,
634              word Ref)
635 {
636   ENTITY  * this;
637   byte e_no;
638
639 #ifdef  USE_EXTENDED_DEBUGS
640         {
641                 ISDN_ADAPTER *io = (ISDN_ADAPTER *)a->io ;
642                 DPRINTF(("IDI: <A%d Id=0x%x Rc=0x%x", io->ANum, Id, Rc))
643         }
644 #else
645   DPRINTF(("IDI: <RC(Rc=%x,Id=%x,Ch=%x)",Rc,Id,Ch));
646 #endif
647
648         /* check for ready interrupt                                */
649   if(Rc==READY_INT) {
650     if(a->ReadyInt) {
651       a->ReadyInt--;
652       return 0;
653     }
654     return 2;
655   }
656
657         /* if we know this Id ...                                   */
658   e_no = a->IdTable[Id];
659   if(e_no) {
660
661     this = entity_ptr(a,e_no);
662
663     this->RcCh = Ch;
664
665         /* if it is a return code to a REMOVE request, remove the   */
666         /* Id from our table                                        */
667     if(this->Req==REMOVE && Rc==OK) {
668       free_entity(a, e_no);
669       a->IdTable[Id] = 0;
670       this->Id = 0;
671 /**************************************************************/
672       if ((this->More & XMOREC) > 1) {
673         this->More &= ~XMOREC;
674         this->More |= 1;
675         DPRINTF(("isdn_rc, Id=%x, correct More on REMOVE", Id));
676       }
677     }
678
679     if (Rc==OK_FC) {
680       this->Rc = Rc;
681       this->More = (this->More & (~XBUSY | XMOREC)) | 1;
682       this->complete = 0xFF;
683       CALLBACK(a, this);
684       return 0;
685     }
686     if(this->More &XMOREC)
687       this->More--;
688
689         /* call the application callback function                   */
690     if(this->More &XMOREF && !(this->More &XMOREC)) {
691       this->Rc = Rc;
692       this->More &=~XBUSY;
693       this->complete=0xff;
694       CALLBACK(a, this);
695     }
696     return 0;
697   }
698
699         /* if it's an ASSIGN return code check if it's a return     */
700         /* code to an ASSIGN request from us                        */
701   if((Rc &0xf0)==ASSIGN_RC) {
702
703     e_no = get_assign(a, Ref);
704
705     if(e_no) {
706
707       this = entity_ptr(a,e_no);
708
709       this->Id = Id;
710
711         /* call the application callback function                   */
712       this->Rc = Rc;
713       this->More &=~XBUSY;
714       this->complete=0xff;
715       CALLBACK(a, this);
716
717       if(Rc==ASSIGN_OK) {
718         a->IdTable[Id] = e_no;
719       }
720       else
721       {
722         free_entity(a, e_no);
723         a->IdTable[Id] = 0;
724         this->Id = 0;
725       }
726       return 1;
727     }
728   }
729   return 2;
730 }
731
732 /*------------------------------------------------------------------*/
733 /* indication handler                                               */
734 /*------------------------------------------------------------------*/
735
736 static
737 byte isdn_ind(ADAPTER * a,
738               byte Ind,
739               byte Id,
740               byte Ch,
741               PBUFFER * RBuffer,
742               byte MInd,
743               word MLength)
744 {
745   ENTITY  * this;
746   word clength;
747   word offset;
748   BUFFERS  *R;
749
750 #ifdef  USE_EXTENDED_DEBUGS
751         {
752                 ISDN_ADAPTER *io = (ISDN_ADAPTER *)a->io ;
753                 DPRINTF(("IDI: <A%d Id=0x%x Ind=0x%x", io->ANum, Id, Ind))
754         }
755 #else
756   DPRINTF(("IDI: <IND(Ind=%x,Id=%x,Ch=%x)",Ind,Id,Ch));
757 #endif
758
759   if(a->IdTable[Id]) {
760
761     this = entity_ptr(a,a->IdTable[Id]);
762
763     this->IndCh = Ch;
764
765         /* if the Receive More flag is not yet set, this is the     */
766         /* first buffer of the packet                               */
767     if(this->RCurrent==0xff) {
768
769         /* check for receive buffer chaining                        */
770       if(Ind==this->MInd) {
771         this->complete = 0;
772         this->Ind = MInd;
773       }
774       else {
775         this->complete = 1;
776         this->Ind = Ind;
777       }
778
779         /* call the application callback function for the receive   */
780         /* look ahead                                               */
781       this->RLength = MLength;
782
783       a->ram_look_ahead(a, RBuffer, this);
784
785       this->RNum = 0;
786       CALLBACK(a, this);
787
788         /* map entity ptr, selector could be re-mapped by call to   */
789         /* IDI from within callback                                 */
790       this = entity_ptr(a,a->IdTable[Id]);
791
792         /* check for RNR                                            */
793       if(this->RNR==1) {
794         this->RNR = 0;
795         return 1;
796       }
797
798         /* if no buffers are provided by the application, the       */
799         /* application want to copy the data itself including       */
800         /* N_MDATA/LL_MDATA chaining                                */
801       if(!this->RNR && !this->RNum) {
802         return 0;
803       }
804
805         /* if there is no RNR, set the More flag                    */
806       this->RCurrent = 0;
807       this->ROffset = 0;
808     }
809
810     if(this->RNR==2) {
811       if(Ind!=this->MInd) {
812         this->RCurrent = 0xff;
813         this->RNR = 0;
814       }
815       return 0;
816     }
817         /* if we have received buffers from the application, copy   */
818         /* the data into these buffers                              */
819     offset = 0;
820     R = PTR_R(a,this);
821     do {
822       if(this->ROffset==R[this->RCurrent].PLength) {
823         this->ROffset = 0;
824         this->RCurrent++;
825       }
826       clength = a->ram_inw(a, &RBuffer->length)-offset;
827       if (clength > R[this->RCurrent].PLength-this->ROffset)
828               clength = R[this->RCurrent].PLength-this->ROffset;
829       if(R[this->RCurrent].P) {
830         a->ram_in_buffer(a,
831                          &RBuffer->P[offset],
832                          PTR_P(a,this,&R[this->RCurrent].P[this->ROffset]),
833                          clength);
834       }
835       offset +=clength;
836       this->ROffset +=clength;
837     } while(offset<(a->ram_inw(a, &RBuffer->length)));
838
839         /* if it's the last buffer of the packet, call the          */
840         /* application callback function for the receive complete   */
841         /* call                                                     */
842     if(Ind!=this->MInd) {
843       R[this->RCurrent].PLength = this->ROffset;
844       if(this->ROffset) this->RCurrent++;
845       this->RNum = this->RCurrent;
846       this->RCurrent = 0xff;
847       this->Ind = Ind;
848       this->complete = 2;
849       CALLBACK(a, this);
850     }
851     return 0;
852   }
853   return 2;
854 }