idi.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:19k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

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