File: /usr/src/linux/drivers/isdn/eicon/idi.c

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