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