2 * PROJECT: ReactOS Drivers
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: drivers/sac/driver/conmgr.c
5 * PURPOSE: Driver for the Server Administration Console (SAC) for EMS
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES *******************************************************************/
13 /* GLOBALS ********************************************************************/
15 DEFINE_GUID(PRIMARY_SAC_CHANNEL_APPLICATION_GUID
,
19 0xBC, 0xCF, 0x80, 0x6D, 0x61, 0x72, 0x69, 0x6F);
21 LONG CurrentChannelRefCount
;
22 KMUTEX CurrentChannelLock
;
24 PSAC_CHANNEL CurrentChannel
;
25 PSAC_CHANNEL SacChannel
;
27 ULONG ExecutePostConsumerCommand
;
28 PSAC_CHANNEL ExecutePostConsumerCommandData
;
30 BOOLEAN InputInEscape
, InputInEscTab
, ConMgrLastCharWasCR
;
33 BOOLEAN GlobalPagingNeeded
, GlobalDoThreads
;
35 /* FUNCTIONS ******************************************************************/
39 SacPutString(IN PWCHAR String
)
43 /* Write the string on the main SAC channel */
44 Status
= ChannelOWrite(SacChannel
,
46 wcslen(String
) * sizeof(WCHAR
));
47 if (!NT_SUCCESS(Status
))
49 SAC_DBG(SAC_DBG_INIT
, "SAC XmlMgrSacPutString: OWrite failed\n");
55 SacPutSimpleMessage(IN ULONG MessageIndex
)
61 MessageBuffer
= GetMessage(MessageIndex
);
65 SacPutString(MessageBuffer
);
79 ConMgrDisplayCurrentChannel(VOID
)
84 /* Make sure the lock is held */
85 SacAssertMutexLockHeld();
87 /* Check if we can redraw */
88 Status
= ChannelHasRedrawEvent(CurrentChannel
, &HasRedraw
);
89 if (NT_SUCCESS(Status
))
92 _InterlockedExchange(&CurrentChannel
->WriteEnabled
, 1);
95 /* If we can redraw, set the event */
96 ChannelSetRedrawEvent(CurrentChannel
);
99 /* Flush the output */
100 Status
= ChannelOFlush(CurrentChannel
);
103 /* All done, return the status */
109 ConMgrWriteData(IN PSAC_CHANNEL Channel
,
111 IN ULONG BufferLength
)
115 LARGE_INTEGER Interval
;
117 /* Loop up to 32 times */
118 for (i
= 0; i
< 32; i
++)
120 /* Attempt sending the data */
121 Status
= HeadlessDispatch(HeadlessCmdPutData
, Buffer
, BufferLength
, NULL
, NULL
);
122 if (Status
!= STATUS_UNSUCCESSFUL
) break;
124 /* Sending the data on the port failed, wait a second... */
125 Interval
.HighPart
= -1;
126 Interval
.LowPart
= -100000;
127 KeDelayExecutionThread(KernelMode
, FALSE
, &Interval
);
130 /* After 32 attempts it should really have worked... */
131 ASSERT(NT_SUCCESS(Status
));
137 ConMgrFlushData(IN PSAC_CHANNEL Channel
)
140 return STATUS_SUCCESS
;
145 ConMgrIsSacChannel(IN PSAC_CHANNEL Channel
)
147 /* Check which channel is active */
148 return Channel
== SacChannel
;
153 ConMgrIsWriteEnabled(IN PSAC_CHANNEL Channel
)
155 /* If the current channel is active, allow writes */
156 return ChannelIsEqual(Channel
, &CurrentChannel
->ChannelId
);
161 ConMgrInitialize(VOID
)
164 PSAC_CHANNEL FoundChannel
;
165 SAC_CHANNEL_ATTRIBUTES SacChannelAttributes
;
168 /* Initialize the connection manager lock */
169 SacInitializeMutexLock();
170 SacAcquireMutexLock();
172 /* Setup the attributes for the raw SAC channel */
173 RtlZeroMemory(&SacChannelAttributes
, sizeof(SacChannelAttributes
));
174 SacChannelAttributes
.ChannelType
= VtUtf8
;
176 /* Get the right name for it */
177 pcwch
= GetMessage(SAC_CHANNEL_NAME
);
179 wcsncpy(SacChannelAttributes
.NameBuffer
, pcwch
, SAC_CHANNEL_NAME_SIZE
);
180 SacChannelAttributes
.NameBuffer
[SAC_CHANNEL_NAME_SIZE
] = ANSI_NULL
;
182 /* Get the right description for it */
183 pcwch
= GetMessage(SAC_CHANNEL_DESCRIPTION
);
185 wcsncpy(SacChannelAttributes
.DescriptionBuffer
, pcwch
, SAC_CHANNEL_DESCRIPTION_SIZE
);
186 SacChannelAttributes
.DescriptionBuffer
[SAC_CHANNEL_DESCRIPTION_SIZE
] = ANSI_NULL
;
188 /* Set all the right flags */
189 SacChannelAttributes
.Flag
= SAC_CHANNEL_FLAG_APPLICATION
| SAC_CHANNEL_FLAG_INTERNAL
;
190 SacChannelAttributes
.CloseEvent
= NULL
;
191 SacChannelAttributes
.HasNewDataEvent
= NULL
;
192 SacChannelAttributes
.LockEvent
= NULL
;
193 SacChannelAttributes
.RedrawEvent
= NULL
;
194 SacChannelAttributes
.ChannelId
= PRIMARY_SAC_CHANNEL_APPLICATION_GUID
;
197 Status
= ChanMgrCreateChannel(&SacChannel
, &SacChannelAttributes
);
198 if (NT_SUCCESS(Status
))
200 /* Try to get it back */
201 Status
= ChanMgrGetByHandle(SacChannel
->ChannelId
, &FoundChannel
);
202 if (NT_SUCCESS(Status
))
204 /* Set it as the current and SAC channel */
205 SacChannel
= CurrentChannel
= FoundChannel
;
207 /* Diasable writes for now and clear the display */
208 _InterlockedExchange(&FoundChannel
->WriteEnabled
, FALSE
);
209 Status
= HeadlessDispatch(HeadlessCmdClearDisplay
, NULL
, 0, NULL
, NULL
);
210 if (!NT_SUCCESS(Status
))
212 SAC_DBG(SAC_DBG_INIT
, "SAC ConMgrInitialize: Failed dispatch\n");
215 /* Display the initial prompt */
216 SacPutSimpleMessage(SAC_NEWLINE
);
217 SacPutSimpleMessage(SAC_INIT_STATUS
);
218 SacPutSimpleMessage(SAC_NEWLINE
);
219 SacPutSimpleMessage(SAC_PROMPT
);
221 /* Display the current channel */
222 ConMgrDisplayCurrentChannel();
226 /* Release the channel lock */
227 SacReleaseMutexLock();
228 return STATUS_SUCCESS
;
233 ConMgrEventMessage(IN PWCHAR EventMessage
,
236 /* Acquire the current channel lock if needed */
237 if (!LockHeld
) SacAcquireMutexLock();
239 /* Send out the event message */
240 SacPutSimpleMessage(2);
241 SacPutString(EventMessage
);
242 SacPutSimpleMessage(3);
244 /* Release the current channel lock if needed */
245 if (!LockHeld
) SacReleaseMutexLock();
250 ConMgrSimpleEventMessage(IN ULONG MessageIndex
,
253 PWCHAR MessageBuffer
;
256 /* Get the message to send out */
257 MessageBuffer
= GetMessage(MessageIndex
);
261 ConMgrEventMessage(MessageBuffer
, LockHeld
);
266 /* It doesn't exist, fail */
270 /* Return if the message was sent or not */
276 ConMgrDisplayFastChannelSwitchingInterface(IN PSAC_CHANNEL Channel
)
280 return STATUS_NOT_IMPLEMENTED
;
285 ConMgrSetCurrentChannel(IN PSAC_CHANNEL Channel
)
288 BOOLEAN HasRedrawEvent
;
290 /* Make sure the lock is held */
291 SacAssertMutexLockHeld();
293 /* Check if we have a redraw event */
294 Status
= ChannelHasRedrawEvent(CurrentChannel
, &HasRedrawEvent
);
295 if (!NT_SUCCESS(Status
)) return Status
;
298 if (HasRedrawEvent
) ChannelClearRedrawEvent(CurrentChannel
);
300 /* Disable writes on the current channel */
301 _InterlockedExchange(&CurrentChannel
->WriteEnabled
, 0);
303 /* Release the current channel */
304 Status
= ChanMgrReleaseChannel(CurrentChannel
);
305 if (!NT_SUCCESS(Status
)) return Status
;
307 /* Set the new channel and also disable writes on it */
308 CurrentChannel
= Channel
;
309 _InterlockedExchange(&Channel
->WriteEnabled
, 0);
310 return STATUS_SUCCESS
;
315 ConMgrResetCurrentChannel(IN BOOLEAN KeepChannel
)
318 PSAC_CHANNEL Channel
;
320 /* Make sure the lock is held */
321 SacAssertMutexLockHeld();
323 /* Get the current SAC channel */
324 Status
= ChanMgrGetByHandle(SacChannel
->ChannelId
, &Channel
);
325 if (NT_SUCCESS(Status
))
327 /* Set this as the current SAC channel*/
328 SacChannel
= Channel
;
329 Status
= ConMgrSetCurrentChannel(Channel
);
330 if (NT_SUCCESS(Status
))
332 /* Check if the caller wants to switch or not */
335 /* Nope, keep the same channel */
336 Status
= ConMgrDisplayCurrentChannel();
340 /* Yep, show the switching interface */
341 Status
= ConMgrDisplayFastChannelSwitchingInterface(CurrentChannel
);
352 ConMgrChannelClose(IN PSAC_CHANNEL Channel
)
354 NTSTATUS Status
= STATUS_SUCCESS
;
356 /* Check if we're in the right channel */
357 if (ConMgrIsWriteEnabled(Channel
))
360 Status
= ConMgrResetCurrentChannel(FALSE
);
361 ASSERT(NT_SUCCESS(Status
));
374 /* Check if we have a SAC channel */
378 Status
= ChannelClose(SacChannel
);
379 if (!NT_SUCCESS(Status
))
381 SAC_DBG(SAC_DBG_INIT
, "SAC ConMgrShutdown: failed closing SAC channel.\n");
384 /* No longer have one */
388 /* Check if we have a current channel */
392 Status
= ChanMgrReleaseChannel(CurrentChannel
);
393 if (!NT_SUCCESS(Status
))
395 SAC_DBG(SAC_DBG_INIT
, "SAC ConMgrShutdown: failed releasing current channel\n");
398 /* No longer have one */
399 CurrentChannel
= NULL
;
403 return STATUS_SUCCESS
;
408 ConMgrAdvanceCurrentChannel(VOID
)
412 PSAC_CHANNEL Channel
;
414 /* Should always be called with the lock held */
415 SacAssertMutexLockHeld();
417 /* Get the next active channel */
418 Status
= ChanMgrGetNextActiveChannel(CurrentChannel
, &Index
, &Channel
);
419 if (NT_SUCCESS(Status
))
421 /* Set it as the new channel */
422 Status
= ConMgrSetCurrentChannel(Channel
);
423 if (NT_SUCCESS(Status
))
425 /* Let the user switch to it */
426 Status
= ConMgrDisplayFastChannelSwitchingInterface(Channel
);
436 ConMgrChannelOWrite(IN PSAC_CHANNEL Channel
,
437 IN PVOID WriteBuffer
)
441 /* Do the write with the lock held */
442 SacAcquireMutexLock();
444 Status
= STATUS_NOT_IMPLEMENTED
;// ChannelOWrite(Channel, WriteBuffer + 24, *(WriteBuffer + 20));
445 SacReleaseMutexLock();
447 /* Return back to the caller */
448 ASSERT(NT_SUCCESS(Status
) || Status
== STATUS_NOT_FOUND
);
454 ConMgrProcessInputLine(VOID
)
456 BOOLEAN EnablePaging
;
459 SAC_DBG(4, "SAC Input Test: %s\n", InputBuffer
);
461 if (!strncmp(InputBuffer
, "t", 1))
465 else if (!strncmp(InputBuffer
, "?", 1))
469 else if (!strncmp(InputBuffer
, "help", 4))
473 else if (!strncmp(InputBuffer
, "f", 1))
477 else if (!strncmp(InputBuffer
, "p", 1))
481 else if (!strncmp(InputBuffer
, "id", 2))
483 DoMachineInformationCommand();
485 else if (!strncmp(InputBuffer
, "crashdump", 9))
489 else if (!strncmp(InputBuffer
, "lock", 4))
493 else if (!strncmp(InputBuffer
, "shutdown", 8))
495 ExecutePostConsumerCommand
= Shutdown
;
497 else if (!strncmp(InputBuffer
, "restart", 7))
499 ExecutePostConsumerCommand
= Restart
;
501 else if (!strncmp(InputBuffer
, "d", 1))
503 EnablePaging
= GlobalPagingNeeded
;
504 Status
= HeadlessDispatch(HeadlessCmdDisplayLog
,
506 sizeof(EnablePaging
),
509 if (!NT_SUCCESS(Status
)) SAC_DBG(4, "SAC Display Log failed.\n");
511 else if (!strncmp(InputBuffer
, "cmd", 3))
513 if (CommandConsoleLaunchingEnabled
)
515 DoCmdCommand(InputBuffer
);
519 SacPutSimpleMessage(148);
522 else if (!(strncmp(InputBuffer
, "ch", 2)) &&
523 (((strlen(InputBuffer
) > 1) && (InputBuffer
[2] == ' ')) ||
524 (strlen(InputBuffer
) == 2)))
526 DoChannelCommand(InputBuffer
);
528 else if (!(strncmp(InputBuffer
, "k", 1)) &&
529 (((strlen(InputBuffer
) > 1) && (InputBuffer
[1] == ' ')) ||
530 (strlen(InputBuffer
) == 1)))
532 DoKillCommand(InputBuffer
);
534 else if (!(strncmp(InputBuffer
, "l", 1)) &&
535 (((strlen(InputBuffer
) > 1) && (InputBuffer
[1] == ' ')) ||
536 (strlen(InputBuffer
) == 1)))
538 DoLowerPriorityCommand(InputBuffer
);
540 else if (!(strncmp(InputBuffer
, "r", 1)) &&
541 (((strlen(InputBuffer
) > 1) && (InputBuffer
[1] == ' ')) ||
542 (strlen(InputBuffer
) == 1)))
544 DoRaisePriorityCommand(InputBuffer
);
546 else if (!(strncmp(InputBuffer
, "m", 1)) &&
547 (((strlen(InputBuffer
) > 1) && (InputBuffer
[1] == ' ')) ||
548 (strlen(InputBuffer
) == 1)))
550 DoLimitMemoryCommand(InputBuffer
);
552 else if (!(strncmp(InputBuffer
, "s", 1)) &&
553 (((strlen(InputBuffer
) > 1) && (InputBuffer
[1] == ' ')) ||
554 (strlen(InputBuffer
) == 1)))
556 DoSetTimeCommand(InputBuffer
);
558 else if (!(strncmp(InputBuffer
, "i", 1)) &&
559 (((strlen(InputBuffer
) > 1) && (InputBuffer
[1] == ' ')) ||
560 (strlen(InputBuffer
) == 1)))
562 DoSetIpAddressCommand(InputBuffer
);
564 else if ((InputBuffer
[0] != '\n') && (InputBuffer
[0] != ANSI_NULL
))
566 SacPutSimpleMessage(SAC_UNKNOWN_COMMAND
);
572 ConMgrSerialPortConsumer(VOID
)
578 ULONG ReadBufferSize
, i
;
579 WCHAR StringBuffer
[2];
580 SAC_DBG(SAC_DBG_MACHINE
, "SAC TimerDpcRoutine: Entering.\n"); //bug
582 /* Acquire the manager lock and make sure a channel is selected */
583 SacAcquireMutexLock();
584 ASSERT(CurrentChannel
);
586 /* Read whatever came off the serial port */
587 for (Status
= SerialBufferGetChar(&Char
);
589 Status
= SerialBufferGetChar(&Char
))
591 /* If nothing came through, bail out */
592 if (Status
== STATUS_NO_DATA_DETECTED
) break;
594 /* Check if ESC was pressed */
597 /* Was it already pressed? */
600 /* First time ESC is pressed! Remember and reset TAB state */
601 InputInEscTab
= FALSE
;
602 InputInEscape
= TRUE
;
606 else if (Char
== '\t')
608 /* TAB was pressed, is it following ESC (VT-100 sequence)? */
611 /* Yes! This must be the only ESC-TAB we see in once moment */
612 ASSERT(InputInEscTab
== FALSE
);
614 /* No longer treat us as being in ESC */
615 InputInEscape
= FALSE
;
617 /* ESC-TAB is the sequence for changing channels */
618 Status
= ConMgrAdvanceCurrentChannel();
619 if (!NT_SUCCESS(Status
)) break;
621 /* Remember ESC-TAB was pressed */
622 InputInEscTab
= TRUE
;
626 else if ((Char
== '0') && (InputInEscTab
))
628 /* It this ESC-TAB-0? */
629 ASSERT(InputInEscape
== FALSE
);
630 InputInEscTab
= FALSE
;
632 /* If writes are already enabled, don't do this */
633 if (!CurrentChannel
->WriteEnabled
)
635 /* Reset the channel, this is our special sequence */
636 Status
= ConMgrResetCurrentChannel(FALSE
);
637 if (!NT_SUCCESS(Status
)) break;
644 /* This is ESC-TAB-something else */
645 InputInEscTab
= FALSE
;
647 /* If writes are already enabled, don't do this */
648 if (!CurrentChannel
->WriteEnabled
)
650 /* Display the current channel */
651 InputInEscape
= FALSE
;
652 Status
= ConMgrDisplayCurrentChannel();
653 if (!NT_SUCCESS(Status
)) break;
658 /* Check if an ESC-sequence was being typed into a command channel */
659 if ((InputInEscape
) && (CurrentChannel
!= SacChannel
))
661 /* Store the ESC in the current channel buffer */
662 ReadBuffer
[0] = '\x1B';
663 ChannelIWrite(CurrentChannel
, ReadBuffer
, sizeof(CHAR
));
666 /* Check if we are no longer pressing ESC and exit the mode if so */
667 if (Char
!= '\x1B') InputInEscape
= FALSE
;
669 /* Whatever was typed in, save it int eh current channel */
670 ChannelIWrite(CurrentChannel
, &Char
, sizeof(Char
));
672 /* If this is a command channel, we're done, nothing to process */
673 if (CurrentChannel
!= SacChannel
) continue;
675 /* Check for line feed right after a carriage return */
676 if ((ConMgrLastCharWasCR
) && (Char
== '\n'))
678 /* Ignore the line feed, but clear the carriage return */
679 ChannelIReadLast(CurrentChannel
);
680 ConMgrLastCharWasCR
= 0;
684 /* Check if the user did a carriage return */
685 ConMgrLastCharWasCR
= (Char
== '\n');
687 /* If the user did an "ENTER", we need to run the command */
688 if ((Char
== '\n') || (Char
== '\r'))
690 /* Echo back to the terminal */
691 SacPutString(L
"\r\n");
694 /* Inhibit the character (either CR or LF) */
695 ChannelIReadLast(CurrentChannel
);
697 /* NULL-terminate the channel's input buffer */
698 ReadBuffer
[0] = ANSI_NULL
;
699 ChannelIWrite(CurrentChannel
, ReadBuffer
, sizeof(CHAR
));
701 /* Loop over every last character */
704 /* Read every character in the channel, and strip whitespace */
705 LastChar
= ChannelIReadLast(CurrentChannel
);
706 ReadBuffer
[0] = (CHAR
) LastChar
;
707 } while ((!(LastChar
) ||
708 (LastChar
== L
' ') ||
709 (LastChar
== L
'\t')) &&
710 (ChannelIBufferLength(CurrentChannel
)));
712 /* Write back into the channel the last character */
713 ChannelIWrite(CurrentChannel
, ReadBuffer
, sizeof(CHAR
));
715 /* NULL-terminate the input buffer */
716 ReadBuffer
[0] = ANSI_NULL
;
717 ChannelIWrite(CurrentChannel
, ReadBuffer
, sizeof(CHAR
));
719 /* Now loop over every first character */
722 /* Read every character in the channel, and strip whitespace */
723 ChannelIRead(CurrentChannel
,
727 } while ((ReadBufferSize
) &&
728 ((ReadBuffer
[0] == ' ') || (ReadBuffer
[0] == '\t')));
730 /* We read one more than we should, so treat that as our first one */
731 InputBuffer
[0] = ReadBuffer
[0];
734 /* And now loop reading all the others */
737 /* Read each character -- there should be max 80 */
738 ChannelIRead(CurrentChannel
,
742 ASSERT(i
< SAC_VTUTF8_COL_WIDTH
);
743 InputBuffer
[i
++] = ReadBuffer
[0];
744 } while (ReadBufferSize
);
746 /* Now go over the entire input stream */
747 for (i
= 0; InputBuffer
[i
]; i
++)
749 /* Again it should be less than 80 characters */
750 ASSERT(i
< SAC_VTUTF8_COL_WIDTH
);
752 /* And downbase each character */
753 Char
= InputBuffer
[i
];
754 if ((Char
>= 'A') && (Char
<= 'Z')) InputBuffer
[i
] = Char
+ ' ';
757 /* Ok, at this point, no pending command should exist */
758 ASSERT(ExecutePostConsumerCommand
== Nothing
);
760 /* Go and process the input, then show the prompt again */
761 ConMgrProcessInputLine();
762 SacPutSimpleMessage(SAC_PROMPT
);
764 /* If the user typed a valid command, get out of here */
765 if (ExecutePostConsumerCommand
!= Nothing
) break;
771 /* Check if the user typed backspace or delete */
772 if ((Char
== '\b') || (Char
== '\x7F'))
774 /* Omit the last character, which should be the DEL/BS itself */
775 if (ChannelIBufferLength(CurrentChannel
))
777 ChannelIReadLast(CurrentChannel
);
780 /* Omit the before-last character, which is the one to delete */
781 if (ChannelIBufferLength(CurrentChannel
))
783 /* Also send two backspaces back to the console */
784 SacPutString(L
"\b \b");
785 ChannelIReadLast(CurrentChannel
);
792 /* If the user pressed CTRL-C at this point, treat it like ENTER */
793 if (Char
== '\x03') goto DoLineParsing
;
795 /* Check if the user pressed TAB */
798 /* Omit it, send a BELL, and keep going. We ignore TABs */
799 ChannelIReadLast(CurrentChannel
);
804 /* Check if the user is getting close to the end of the screen */
805 if (ChannelIBufferLength(CurrentChannel
) == (SAC_VTUTF8_COL_WIDTH
- 2))
807 /* Delete the last character, replacing it with this one instead */
808 swprintf(StringBuffer
, L
"\b%c", Char
);
809 SacPutString(StringBuffer
);
811 /* Omit the last two characters from the buffer */
812 ChannelIReadLast(CurrentChannel
);
813 ChannelIReadLast(CurrentChannel
);
815 /* Write the last character that was just typed in */
816 ReadBuffer
[0] = Char
;
817 ChannelIWrite(CurrentChannel
, ReadBuffer
, sizeof(CHAR
));
821 /* Nothing of interest happened, just write the character back */
822 swprintf(StringBuffer
, L
"%c", Char
);
823 SacPutString(StringBuffer
);
826 /* We're done, release the lock */
827 SacReleaseMutexLock();
828 SAC_DBG(SAC_DBG_MACHINE
, "SAC TimerDpcRoutine: Exiting.\n"); //bug
833 ConMgrWorkerProcessEvents(IN PSAC_DEVICE_EXTENSION DeviceExtension
)
835 SAC_DBG(SAC_DBG_ENTRY_EXIT
, "SAC WorkerProcessEvents: Entering.\n");
837 /* Enter the main loop */
840 /* Wait for something to do */
841 KeWaitForSingleObject(&DeviceExtension
->Event
,
847 /* Consume data off the serial port */
848 ConMgrSerialPortConsumer();
849 switch (ExecutePostConsumerCommand
)
852 /* A reboot was sent, do it */
853 DoRebootCommand(FALSE
);
857 /* A close was sent, do it */
858 ChanMgrCloseChannel(ExecutePostConsumerCommandData
);
859 ChanMgrReleaseChannel(ExecutePostConsumerCommandData
);
863 /* A shutdown was sent, do it */
864 DoRebootCommand(TRUE
);
868 /* Clear the serial port consumer state */
869 ExecutePostConsumerCommand
= Nothing
;
870 ExecutePostConsumerCommandData
= NULL
;
876 ConMgrGetChannelCloseMessage(IN PSAC_CHANNEL Channel
,
877 IN NTSTATUS CloseStatus
,
878 OUT PWCHAR OutputBuffer
)
881 return STATUS_NOT_IMPLEMENTED
;
886 ConMgrHandleEvent(IN ULONG EventCode
,
887 IN PSAC_CHANNEL Channel
,
891 return STATUS_NOT_IMPLEMENTED
;