update for HEAD-2003021201
[reactos.git] / drivers / net / npf / openclos.c
1 /*
2  * Copyright (c) 1999, 2000
3  *      Politecnico di Torino.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that: (1) source code distributions
7  * retain the above copyright notice and this paragraph in its entirety, (2)
8  * distributions including binary code include the above copyright notice and
9  * this paragraph in its entirety in the documentation or other materials
10  * provided with the distribution, and (3) all advertising materials mentioning
11  * features or use of this software display the following acknowledgement:
12  * ``This product includes software developed by the Politecnico
13  * di Torino, and its contributors.'' Neither the name of
14  * the University nor the names of its contributors may be used to endorse
15  * or promote products derived from this software without specific prior
16  * written permission.
17  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20  */
21
22 #ifdef _MSC_VER
23 #include "ntddk.h"
24 #include "ntiologc.h"
25 #include "ndis.h"
26 #else
27 #include <ddk/ntddk.h>
28 #include <net/ndis.h>
29 #endif
30 #include "debug.h"
31 #include "packet.h"
32
33 static NDIS_MEDIUM MediumArray[] = {
34         NdisMedium802_3,
35         NdisMediumWan,
36         NdisMediumFddi,
37         NdisMediumArcnet878_2,
38         NdisMediumAtm,
39         NdisMedium802_5
40 };
41
42 #define NUM_NDIS_MEDIA  (sizeof MediumArray / sizeof MediumArray[0])
43
44 ULONG NamedEventsCounter=0;
45
46 //Itoa. Replaces the buggy RtlIntegerToUnicodeString
47 void PacketItoa(UINT n,PUCHAR buf){
48 int i;
49
50         for(i=0;i<20;i+=2){
51                 buf[18-i]=(n%10)+48;
52                 buf[19-i]=0;
53                 n/=10;
54         }
55
56 }
57
58 /// Global start time. Used as an absolute reference for timestamp conversion.
59 struct time_conv G_Start_Time = {
60         0,      
61         {0, 0}, 
62 };
63
64 UINT n_Opened_Instances = 0;
65
66 NDIS_SPIN_LOCK Opened_Instances_Lock;
67
68 //-------------------------------------------------------------------
69
70 NTSTATUS STDCALL
71 NPF_Open(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
72 {
73
74     PDEVICE_EXTENSION DeviceExtension;
75
76     POPEN_INSTANCE    Open;
77
78     PIO_STACK_LOCATION  IrpSp;
79
80     NDIS_STATUS     Status;
81     NDIS_STATUS     ErrorStatus;
82     UINT            i;
83         PUCHAR                  tpointer;
84     PLIST_ENTRY     PacketListEntry;
85         PCHAR                   EvName;
86
87     IF_LOUD(DbgPrint("NPF: OpenAdapter\n");)
88
89     DeviceExtension = DeviceObject->DeviceExtension;
90
91
92     IrpSp = IoGetCurrentIrpStackLocation(Irp);
93
94     //  allocate some memory for the open structure
95 #define NPF_TAG_OPENSTRUCT  TAG('0', 'O', 'W', 'A')
96     Open=ExAllocatePoolWithTag(NonPagedPool, sizeof(OPEN_INSTANCE), NPF_TAG_OPENSTRUCT);
97
98
99     if (Open==NULL) {
100         // no memory
101         Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
102         IoCompleteRequest(Irp, IO_NO_INCREMENT);
103         return STATUS_INSUFFICIENT_RESOURCES;
104     }
105
106     RtlZeroMemory(
107         Open,
108         sizeof(OPEN_INSTANCE)
109         );
110
111
112 #define NPF_TAG_EVNAME  TAG('1', 'O', 'W', 'A')
113         EvName=ExAllocatePoolWithTag(NonPagedPool, sizeof(L"\\BaseNamedObjects\\NPF0000000000"), NPF_TAG_EVNAME);
114
115     if (EvName==NULL) {
116         // no memory
117         ExFreePool(Open);
118             Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
119         IoCompleteRequest(Irp, IO_NO_INCREMENT);
120         return STATUS_INSUFFICIENT_RESOURCES;
121     }
122
123     //  Save or open here
124     IrpSp->FileObject->FsContext=Open;
125         
126     Open->DeviceExtension=DeviceExtension;
127         
128         
129     //  Save the Irp here for the completeion routine to retrieve
130     Open->OpenCloseIrp=Irp;
131         
132     //  Allocate a packet pool for our xmit and receive packets
133     NdisAllocatePacketPool(
134         &Status,
135         &Open->PacketPool,
136         TRANSMIT_PACKETS,
137         sizeof(PACKET_RESERVED));
138         
139         
140     if (Status != NDIS_STATUS_SUCCESS) {
141                 
142         IF_LOUD(DbgPrint("NPF: Failed to allocate packet pool\n");)
143                         
144                 ExFreePool(Open);
145                 ExFreePool(EvName);
146         Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
147         IoCompleteRequest(Irp, IO_NO_INCREMENT);
148         return STATUS_INSUFFICIENT_RESOURCES;
149     }
150
151
152         RtlCopyBytes(EvName,L"\\BaseNamedObjects\\NPF0000000000",sizeof(L"\\BaseNamedObjects\\NPF0000000000"));
153
154         //Create the string containing the name of the read event
155         RtlInitUnicodeString(&Open->ReadEventName,(PCWSTR) EvName);
156
157         PacketItoa(NamedEventsCounter,(PUCHAR)(Open->ReadEventName.Buffer+21));
158
159         InterlockedIncrement(&NamedEventsCounter);
160         
161         IF_LOUD(DbgPrint("\nCreated the named event for the read; name=%ws, counter=%d\n", Open->ReadEventName.Buffer,NamedEventsCounter-1);)
162
163         //allocate the event objects
164         Open->ReadEvent=IoCreateNotificationEvent(&Open->ReadEventName,&Open->ReadEventHandle);
165         if(Open->ReadEvent==NULL){
166                 ExFreePool(Open);
167                 ExFreePool(EvName);
168         Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
169         IoCompleteRequest(Irp, IO_NO_INCREMENT);
170         return STATUS_INSUFFICIENT_RESOURCES;
171         }
172         
173         KeInitializeEvent(Open->ReadEvent, NotificationEvent, FALSE);
174         KeClearEvent(Open->ReadEvent);
175         NdisInitializeEvent(&Open->WriteEvent);
176         NdisInitializeEvent(&Open->IOEvent);
177         NdisInitializeEvent(&Open->DumpEvent);
178         NdisInitializeEvent(&Open->IOEvent);
179         NdisAllocateSpinLock(&Open->machine_lock);
180
181
182     //  list to hold irp's want to reset the adapter
183     InitializeListHead(&Open->ResetIrpList);
184         
185         
186     //  Initialize the request list
187     KeInitializeSpinLock(&Open->RequestSpinLock);
188     InitializeListHead(&Open->RequestList);
189
190         // Initializes the extended memory of the NPF machine
191 #define NPF_TAG_MACHINE  TAG('2', 'O', 'W', 'A')
192         Open->mem_ex.buffer = ExAllocatePoolWithTag(NonPagedPool, DEFAULT_MEM_EX_SIZE, NPF_TAG_MACHINE);
193         if((Open->mem_ex.buffer) == NULL)
194         {
195         // no memory
196         ExFreePool(Open);
197                 ExFreePool(EvName);
198             Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
199         IoCompleteRequest(Irp, IO_NO_INCREMENT);
200         return STATUS_INSUFFICIENT_RESOURCES;
201     }
202         
203         Open->mem_ex.size = DEFAULT_MEM_EX_SIZE;
204         RtlZeroMemory(Open->mem_ex.buffer, DEFAULT_MEM_EX_SIZE);
205         
206         //
207         // Initialize the open instance
208         //
209         Open->BufSize = 0;
210         Open->Buffer = NULL;
211         Open->Bhead = 0;
212         Open->Btail = 0;
213         (INT)Open->BLastByte = -1;
214         Open->Dropped = 0;              //reset the dropped packets counter
215         Open->Received = 0;             //reset the received packets counter
216         Open->Accepted = 0;             //reset the accepted packets counter
217         Open->bpfprogram = NULL;        //reset the filter
218         Open->mode = MODE_CAPT;
219         Open->Nbytes.QuadPart = 0;
220         Open->Npackets.QuadPart = 0;
221         Open->Nwrites = 1;
222         Open->Multiple_Write_Counter = 0;
223         Open->MinToCopy = 0;
224         Open->TimeOut.QuadPart = (LONGLONG)1;
225         Open->Bound = TRUE;
226         Open->DumpFileName.Buffer = NULL;
227         Open->DumpFileHandle = NULL;
228         Open->tme.active = TME_NONE_ACTIVE;
229         Open->DumpLimitReached = FALSE;
230         Open->MaxFrameSize = 0;
231
232         //allocate the spinlock for the statistic counters
233     NdisAllocateSpinLock(&Open->CountersLock);
234
235         //allocate the spinlock for the buffer pointers
236     NdisAllocateSpinLock(&Open->BufLock);
237         
238     //
239     //  link up the request stored in our open block
240     //
241     for (i=0;i<MAX_REQUESTS;i++) {
242         ExInterlockedInsertTailList(
243             &Open->RequestList,
244             &Open->Requests[i].ListElement,
245             &Open->RequestSpinLock);
246                 
247     }
248         
249
250     IoMarkIrpPending(Irp);
251         
252     //
253     //  Try to open the MAC
254     //
255     IF_LOUD(DbgPrint("NPF: Openinig the device %ws, BindingContext=%d\n",DeviceExtension->AdapterName.Buffer, Open);)
256
257         NdisOpenAdapter(
258         &Status,
259         &ErrorStatus,
260         &Open->AdapterHandle,
261         &Open->Medium,
262         MediumArray,
263         NUM_NDIS_MEDIA,
264         DeviceExtension->NdisProtocolHandle,
265         Open,
266         &DeviceExtension->AdapterName,
267         0,
268         NULL);
269
270     IF_LOUD(DbgPrint("NPF: Opened the device, Status=%x\n",Status);)
271
272         if (Status != NDIS_STATUS_PENDING)
273     {
274                 NPF_OpenAdapterComplete(Open,Status,NDIS_STATUS_SUCCESS);
275     }
276         
277     return(STATUS_PENDING);
278 }
279
280 //-------------------------------------------------------------------
281
282 VOID NPF_OpenAdapterComplete(
283         IN NDIS_HANDLE  ProtocolBindingContext,
284     IN NDIS_STATUS  Status,
285     IN NDIS_STATUS  OpenErrorStatus)
286 {
287
288     PIRP                Irp;
289     POPEN_INSTANCE      Open;
290     PLIST_ENTRY                 RequestListEntry;
291         PINTERNAL_REQUEST       MaxSizeReq;
292         NDIS_STATUS                     ReqStatus;
293
294
295     IF_LOUD(DbgPrint("NPF: OpenAdapterComplete\n");)
296
297     Open= (POPEN_INSTANCE)ProtocolBindingContext;
298
299     //
300     //  get the open irp
301     //
302     Irp=Open->OpenCloseIrp;
303
304     if (Status != NDIS_STATUS_SUCCESS) {
305
306         IF_LOUD(DbgPrint("NPF: OpenAdapterComplete-FAILURE\n");)
307
308         NdisFreePacketPool(Open->PacketPool);
309
310                 //free mem_ex
311                 Open->mem_ex.size = 0;
312                 if(Open->mem_ex.buffer != NULL)ExFreePool(Open->mem_ex.buffer);
313
314                 ExFreePool(Open->ReadEventName.Buffer);
315
316                 ZwClose(Open->ReadEventHandle);
317
318
319         ExFreePool(Open);
320     }
321         else {
322                 NdisAcquireSpinLock(&Opened_Instances_Lock);
323                 n_Opened_Instances++;
324                 NdisReleaseSpinLock(&Opened_Instances_Lock);
325                 
326                 IF_LOUD(DbgPrint("Opened Instances:%d", n_Opened_Instances);)
327
328                 // Get the absolute value of the system boot time.
329                 // This is used for timestamp conversion.
330                 TIME_SYNCHRONIZE(&G_Start_Time);
331
332                 // Extract a request from the list of free ones
333                 RequestListEntry=ExInterlockedRemoveHeadList(&Open->RequestList, &Open->RequestSpinLock);
334
335                 if (RequestListEntry == NULL)
336                 {
337
338                     Open->MaxFrameSize = 1514;  // Assume Ethernet
339
340                         Irp->IoStatus.Status = Status;
341                     Irp->IoStatus.Information = 0;
342                     IoCompleteRequest(Irp, IO_NO_INCREMENT);
343
344                     return;
345                 }
346
347                 MaxSizeReq = CONTAINING_RECORD(RequestListEntry, INTERNAL_REQUEST, ListElement);
348                 MaxSizeReq->Irp = Irp;
349                 MaxSizeReq->Internal = TRUE;
350
351                 
352                 MaxSizeReq->Request.RequestType = NdisRequestQueryInformation;
353                 MaxSizeReq->Request.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAXIMUM_TOTAL_SIZE;
354
355                 
356                 MaxSizeReq->Request.DATA.QUERY_INFORMATION.InformationBuffer = &Open->MaxFrameSize;
357                 MaxSizeReq->Request.DATA.QUERY_INFORMATION.InformationBufferLength = 4;
358
359                 //  submit the request
360                 NdisRequest(
361                         &ReqStatus,
362                         Open->AdapterHandle,
363                         &MaxSizeReq->Request);
364
365
366                 if (ReqStatus != NDIS_STATUS_PENDING) {
367                         NPF_RequestComplete(Open, &MaxSizeReq->Request, ReqStatus);
368                 }
369
370                 return;
371
372         }
373
374     Irp->IoStatus.Status = Status;
375     Irp->IoStatus.Information = 0;
376     IoCompleteRequest(Irp, IO_NO_INCREMENT);
377
378     return;
379
380 }
381
382 //-------------------------------------------------------------------
383
384 NTSTATUS STDCALL
385 NPF_Close(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)
386 {
387
388     POPEN_INSTANCE    Open;
389     NDIS_STATUS     Status;
390     PIO_STACK_LOCATION  IrpSp;
391         LARGE_INTEGER ThreadDelay;
392
393     IF_LOUD(DbgPrint("NPF: CloseAdapter\n");)
394
395         IrpSp = IoGetCurrentIrpStackLocation(Irp);
396
397     Open=IrpSp->FileObject->FsContext;
398
399         // Reset the buffer size. This tells the dump thread to stop.
400         Open->BufSize = 0;
401
402         if( Open->Bound == FALSE){
403
404                 NdisWaitEvent(&Open->IOEvent,10000);
405
406                 // Free the filter if it's present
407                 if(Open->bpfprogram != NULL)
408                         ExFreePool(Open->bpfprogram);
409
410                 // Free the jitted filter if it's present
411                 if(Open->Filter != NULL)
412                         BPF_Destroy_JIT_Filter(Open->Filter);
413
414                 //free the buffer
415                 Open->BufSize=0;
416                 if(Open->Buffer != NULL)ExFreePool(Open->Buffer);
417                 
418                 //free mem_ex
419                 Open->mem_ex.size = 0;
420                 if(Open->mem_ex.buffer != NULL)ExFreePool(Open->mem_ex.buffer);
421                                 
422                 NdisFreePacketPool(Open->PacketPool);
423
424                 // Free the string with the name of the dump file
425                 if(Open->DumpFileName.Buffer!=NULL)
426                         ExFreePool(Open->DumpFileName.Buffer);
427                         
428                 ExFreePool(Open->ReadEventName.Buffer);
429                 ExFreePool(Open);
430
431                 Irp->IoStatus.Information = 0;
432                 Irp->IoStatus.Status = STATUS_SUCCESS;
433                 IoCompleteRequest(Irp, IO_NO_INCREMENT);
434                 
435                 return(STATUS_SUCCESS);
436         }
437
438         // Unfreeze the consumer
439         if(Open->mode & MODE_DUMP)
440                 NdisSetEvent(&Open->DumpEvent);
441         else
442                 KeSetEvent(Open->ReadEvent,0,FALSE);
443
444     // Save the IRP
445     Open->OpenCloseIrp = Irp;
446
447     IoMarkIrpPending(Irp);
448  
449         // If this instance is in dump mode, complete the dump and close the file
450         if((Open->mode & MODE_DUMP) && Open->DumpFileHandle != NULL){
451                 NTSTATUS wres;
452
453                 ThreadDelay.QuadPart = -50000000;
454                 // Wait the completion of the thread
455                 wres = KeWaitForSingleObject(Open->DumpThreadObject,
456                                 UserRequest,
457                                 KernelMode,
458                                 TRUE,
459                                 &ThreadDelay);
460
461                 ObDereferenceObject(Open->DumpThreadObject);
462
463
464                 // Flush and close the dump file
465                 NPF_CloseDumpFile(Open);
466         }
467
468         // Destroy the read Event
469         ZwClose(Open->ReadEventHandle);
470
471         // Close the adapter
472         NdisCloseAdapter(
473                 &Status,
474                 Open->AdapterHandle
475                 );
476
477         if (Status != NDIS_STATUS_PENDING) {
478                 
479                 NPF_CloseAdapterComplete(
480                         Open,
481                         Status
482                         );
483                 return STATUS_SUCCESS;
484                 
485         }
486         
487         return(STATUS_PENDING);
488 }
489
490 //-------------------------------------------------------------------
491
492 VOID
493 NPF_CloseAdapterComplete(IN NDIS_HANDLE  ProtocolBindingContext,IN NDIS_STATUS  Status)
494 {
495     POPEN_INSTANCE    Open;
496     PIRP              Irp;
497
498     IF_LOUD(DbgPrint("NPF: CloseAdapterComplete\n");)
499
500     Open= (POPEN_INSTANCE)ProtocolBindingContext;
501
502         // free the allocated structures only if the instance is still bound to the adapter
503         if(Open->Bound == TRUE){
504                 
505                 // Free the filter if it's present
506                 if(Open->bpfprogram != NULL)
507                         ExFreePool(Open->bpfprogram);
508
509                 // Free the jitted filter if it's present
510                 if(Open->Filter != NULL)
511                         BPF_Destroy_JIT_Filter(Open->Filter);
512                 
513                 //free the buffer
514                 Open->BufSize = 0;
515                 if(Open->Buffer!=NULL)ExFreePool(Open->Buffer);
516                 
517                 //free mem_ex
518                 Open->mem_ex.size = 0;
519                 if(Open->mem_ex.buffer != NULL)ExFreePool(Open->mem_ex.buffer);
520                 
521                 NdisFreePacketPool(Open->PacketPool);
522                 
523                 Irp=Open->OpenCloseIrp;
524                 
525                 // Free the string with the name of the dump file
526                 if(Open->DumpFileName.Buffer!=NULL)
527                         ExFreePool(Open->DumpFileName.Buffer);
528
529                 ExFreePool(Open->ReadEventName.Buffer);
530                 ExFreePool(Open);
531                 
532                 // Complete the request only if the instance is still bound to the adapter
533                 Irp->IoStatus.Status = STATUS_SUCCESS;
534                 Irp->IoStatus.Information = 0;
535                 IoCompleteRequest(Irp, IO_NO_INCREMENT);
536         }
537         else
538                 NdisSetEvent(&Open->IOEvent);
539
540         // Decrease the counter of open instances
541         NdisAcquireSpinLock(&Opened_Instances_Lock);
542         n_Opened_Instances--;
543         NdisReleaseSpinLock(&Opened_Instances_Lock);
544
545         IF_LOUD(DbgPrint("Opened Instances:%d", n_Opened_Instances);)
546
547         if(n_Opened_Instances == 0){
548                 // Force a synchronization at the next NPF_Open().
549                 // This hopefully avoids the synchronization issues caused by hibernation or standby.
550                 TIME_DESYNCHRONIZE(&G_Start_Time);
551         }
552
553         return;
554
555 }
556 //-------------------------------------------------------------------
557
558 #ifdef NDIS50
559 NDIS_STATUS
560 NPF_PowerChange(IN NDIS_HANDLE ProtocolBindingContext, IN PNET_PNP_EVENT pNetPnPEvent)
561 {
562     IF_LOUD(DbgPrint("NPF: PowerChange\n");)
563
564         TIME_DESYNCHRONIZE(&G_Start_Time);
565
566         TIME_SYNCHRONIZE(&G_Start_Time);
567
568         return STATUS_SUCCESS;
569 }
570 #endif
571
572 //-------------------------------------------------------------------
573
574 VOID
575 NPF_BindAdapter(
576     OUT PNDIS_STATUS            Status,
577     IN  NDIS_HANDLE             BindContext,
578     IN  PNDIS_STRING            DeviceName,
579     IN  PVOID                   SystemSpecific1,
580     IN  PVOID                   SystemSpecific2
581     )
582 {
583         IF_LOUD(DbgPrint("NPF: NPF_BindAdapter\n");)
584 }
585
586 //-------------------------------------------------------------------
587
588 VOID
589 NPF_UnbindAdapter(
590     OUT PNDIS_STATUS        Status,
591     IN  NDIS_HANDLE         ProtocolBindingContext,
592     IN  NDIS_HANDLE         UnbindContext
593     )
594 {
595     POPEN_INSTANCE   Open =(POPEN_INSTANCE)ProtocolBindingContext;
596         NDIS_STATUS              lStatus;
597
598         IF_LOUD(DbgPrint("NPF: NPF_UnbindAdapter\n");)
599
600         // Reset the buffer size. This tells the dump thread to stop.
601         Open->BufSize=0;
602
603         NdisResetEvent(&Open->IOEvent);
604
605         // This open instance is no more bound to the adapter, set Bound to False
606     InterlockedExchange( (PLONG) &Open->Bound, FALSE );
607
608         // Awake a possible pending read on this instance
609         if(Open->mode & MODE_DUMP)
610                 NdisSetEvent(&Open->DumpEvent);
611         else
612                 KeSetEvent(Open->ReadEvent,0,FALSE);
613
614         // If this instance is in dump mode, complete the dump and close the file
615         if((Open->mode & MODE_DUMP) && Open->DumpFileHandle != NULL)
616                 NPF_CloseDumpFile(Open);
617
618         // Destroy the read Event
619         ZwClose(Open->ReadEventHandle);
620
621     //  close the adapter
622     NdisCloseAdapter(
623         &lStatus,
624         Open->AdapterHandle
625             );
626
627     if (lStatus != NDIS_STATUS_PENDING) {
628
629         NPF_CloseAdapterComplete(
630             Open,
631             lStatus
632             );
633
634                 *Status = NDIS_STATUS_SUCCESS;
635         return;
636
637     }
638
639         *Status = NDIS_STATUS_SUCCESS;
640     return;
641 }
642
643 //-------------------------------------------------------------------
644
645 VOID
646 NPF_ResetComplete(IN NDIS_HANDLE  ProtocolBindingContext,IN NDIS_STATUS  Status)
647
648 {
649     POPEN_INSTANCE      Open;
650     PIRP                Irp;
651
652     PLIST_ENTRY         ResetListEntry;
653
654     IF_LOUD(DbgPrint("NPF: PacketResetComplte\n");)
655
656     Open= (POPEN_INSTANCE)ProtocolBindingContext;
657
658
659     //
660     //  remove the reset IRP from the list
661     //
662     ResetListEntry=ExInterlockedRemoveHeadList(
663                        &Open->ResetIrpList,
664                        &Open->RequestSpinLock
665                        );
666
667 #if DBG
668     if (ResetListEntry == NULL) {
669         DbgBreakPoint();
670         return;
671     }
672 #endif
673
674     Irp=CONTAINING_RECORD(ResetListEntry,IRP,Tail.Overlay.ListEntry);
675
676     Irp->IoStatus.Status = STATUS_SUCCESS;
677     IoCompleteRequest(Irp, IO_NO_INCREMENT);
678
679     IF_LOUD(DbgPrint("NPF: PacketResetComplte exit\n");)
680
681     return;
682
683 }