[SACDRV]: Another attempt to make MSVC happy.
[reactos.git] / reactos / drivers / sac / driver / chanmgr.c
1 /*
2 * PROJECT: ReactOS Drivers
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: drivers/sac/driver/chanmgr.c
5 * PURPOSE: Driver for the Server Administration Console (SAC) for EMS
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include "sacdrv.h"
12
13 /* GLOBALS *******************************************************************/
14
15 SAC_CHANNEL_LOCK ChannelCreateLock;
16 BOOLEAN ChannelCreateEnabled;
17 PSAC_CHANNEL ChannelArray[SAC_MAX_CHANNELS];
18 LONG ChannelRefCount[SAC_MAX_CHANNELS];
19 LONG ChannelReaped[SAC_MAX_CHANNELS];
20 SAC_CHANNEL_LOCK ChannelSlotLock[SAC_MAX_CHANNELS];
21 LONG CurrentChannelRefCount;
22 KMUTEX CurrentChannelLock;
23
24 /* FUNCTIONS *****************************************************************/
25
26 #define MAX_REF_COUNT 100
27
28 #define CHANNEL_SLOT_IS_IN_USE(x) (ChannelRefCount[(x)] > 0)
29
30 FORCEINLINE
31 PSAC_CHANNEL
32 ChannelFromIndex(IN ULONG Index)
33 {
34 return ChannelArray[Index];
35 }
36
37 FORCEINLINE
38 LONG
39 ChannelGetReferenceCount(IN LONG Index)
40 {
41 return ChannelRefCount[Index];
42 }
43
44 FORCEINLINE
45 LONG
46 ChannelReferenceByIndex(IN LONG Index)
47 {
48 if (ChannelGetReferenceCount(Index) > 0)
49 {
50 ASSERT(ChannelRefCount[Index] <= MAX_REF_COUNT);
51 ASSERT(ChannelRefCount[Index] >= 1);
52 _InterlockedIncrement(&ChannelRefCount[Index]);
53 ASSERT(ChannelRefCount[Index] <= MAX_REF_COUNT);
54 ASSERT(ChannelRefCount[Index] >= 2);
55 }
56
57 return ChannelGetReferenceCount(Index);
58 }
59
60 FORCEINLINE
61 LONG
62 ChannelReferenceByIndexWithLock(IN LONG Index)
63 {
64 LONG RefCount;
65
66 ChannelSlotLock(Index);
67 RefCount = ChannelReferenceByIndex(Index);
68 ChannelSlotUnlock(Index);
69 return RefCount;
70 }
71
72 FORCEINLINE
73 LONG
74 ChannelDereferenceByIndex(IN LONG Index)
75 {
76 ASSERT(ChannelGetReferenceCount(Index) <= MAX_REF_COUNT);
77 ASSERT(ChannelGetReferenceCount(Index) > 1);
78 _InterlockedDecrement(&ChannelRefCount[Index]);
79 ASSERT(ChannelGetReferenceCount(Index) >= 1);
80 return ChannelGetReferenceCount(Index);
81 }
82
83 FORCEINLINE
84 VOID
85 ChannelDereferenceByIndexWithLock(IN LONG Index)
86 {
87 ChannelSlotLock(Index);
88 ChannelDereferenceByIndex(Index);
89 ChannelSlotUnlock(Index);
90 }
91
92 FORCEINLINE
93 VOID
94 ChannelDereferenceToZeroByIndex(IN LONG Index)
95 {
96 ASSERT(ChannelGetReferenceCount(Index) == 1);
97 ASSERT(ChannelIsActive(ChannelFromIndex(Index)) == FALSE);
98 _InterlockedExchange(&ChannelRefCount[Index], 0);
99 }
100
101 FORCEINLINE
102 VOID
103 ChannelReferenceToOneByIndex(IN LONG Index)
104 {
105 ASSERT(ChannelGetReferenceCount(Index) == 0);
106 _InterlockedExchange(&ChannelRefCount[Index], 1);
107 }
108
109 FORCEINLINE
110 VOID
111 ChannelReferenceToOneByIndexWithLock(IN LONG Index)
112 {
113 ChannelSlotLock(Index);
114 ASSERT(ChannelGetReferenceCount((Index)) == 1);
115 _InterlockedExchange(&ChannelRefCount[Index], 1);
116 ChannelSlotUnlock(Index);
117 }
118
119 NTSTATUS
120 NTAPI
121 ChanMgrInitialize(VOID)
122 {
123 ULONG i;
124
125 /* Initialize the channel lock */
126 SacInitializeLock(&ChannelCreateLock);
127 ChannelCreateEnabled = TRUE;
128
129 /* Loop through the channel arrays */
130 for (i = 0; i < SAC_MAX_CHANNELS; i++)
131 {
132 /* Clear and initialize their locks */
133 ChannelArray[i] = NULL;
134 SacInitializeLock(&ChannelSlotLock[i]);
135
136 /* Clear their statuses and reference counts */
137 _InterlockedExchange(&ChannelRefCount[i], 0);
138 _InterlockedExchange(&ChannelReaped[i], 1);
139 }
140
141 /* All good */
142 return STATUS_SUCCESS;
143 }
144
145 NTSTATUS
146 NTAPI
147 ChanMgrShutdown(VOID)
148 {
149 /* FIXME: TODO */
150 return STATUS_NOT_IMPLEMENTED;
151 }
152
153 NTSTATUS
154 NTAPI
155 ChanMgrGetChannelByName(IN PWCHAR Name,
156 OUT PSAC_CHANNEL* Channel)
157 {
158 NTSTATUS Status, Status1;
159 ULONG i;
160 PSAC_CHANNEL CurrentChannel;
161 PWCHAR ChannelName;
162 BOOLEAN Found;
163 CHECK_PARAMETER1(Name);
164 CHECK_PARAMETER2(Channel);
165
166 /* Assume failure */
167 *Channel = NULL;
168 Status = STATUS_NOT_FOUND;
169
170 /* Loop through all channels */
171 for (i = 0; i < SAC_MAX_CHANNELS; i++)
172 {
173 /* Reference this one and check if it's valid */
174 if (ChannelReferenceByIndexWithLock(i) > 0)
175 {
176 /* All good, grab it */
177 CurrentChannel = ChannelFromIndex(i);
178 ASSERT(CurrentChannel != NULL);
179
180 /* Get its name */
181 Status1 = ChannelGetName(CurrentChannel, &ChannelName);
182 ASSERT(NT_SUCCESS(Status1));
183
184 /* Check if this is the name that was passed in */
185 Found = wcsicmp(Name, ChannelName);
186 SacFreePool(ChannelName);
187 if (Found)
188 {
189 /* We found it, return it (with a reference held) */
190 *Channel = CurrentChannel;
191 return STATUS_SUCCESS;
192 }
193
194 /* Not the one we want, dereference this one and keep going */
195 ChannelDereferenceByIndexWithLock(i);
196 }
197 }
198
199 /* No channels with this name were found */
200 return Status;
201 }
202
203 NTSTATUS
204 NTAPI
205 ChanMgrGetByHandle(IN SAC_CHANNEL_ID ChannelId,
206 OUT PSAC_CHANNEL* TargetChannel)
207 {
208 NTSTATUS Status;
209 ULONG i;
210 PSAC_CHANNEL Channel;
211 CHECK_PARAMETER2(TargetChannel);
212
213 /* Assume failure */
214 *TargetChannel = NULL;
215 Status = STATUS_NOT_FOUND;
216
217 /* Loop through all channels */
218 for (i = 0; i < SAC_MAX_CHANNELS; i++)
219 {
220 /* Reference this one and check if it's valid */
221 if (ChannelReferenceByIndexWithLock(i) > 0)
222 {
223 /* All good, grab it */
224 Channel = ChannelFromIndex(i);
225 ASSERT(Channel != NULL);
226
227 /* Check if the channel ID matches */
228 if (ChannelIsEqual(Channel, &ChannelId))
229 {
230 /* We found it, return it (with a reference held) */
231 *TargetChannel = Channel;
232 return STATUS_SUCCESS;
233 }
234
235 /* Not the one we want, dereference this one and keep going */
236 ChannelDereferenceByIndexWithLock(i);
237 }
238 }
239
240 /* No channels with this ID were found */
241 return Status;
242 }
243
244 NTSTATUS
245 NTAPI
246 ChanMgrReleaseChannel(IN PSAC_CHANNEL Channel)
247 {
248 LONG Index;
249 ULONG RefCount;
250 PSAC_CHANNEL ThisChannel;
251 CHECK_PARAMETER(Channel);
252
253 /* Get the index of the channel */
254 Index = ChannelGetIndex(Channel);
255
256 /* Drop a reference -- there should still be at least the keepalive left */
257 ChannelSlotLock(Index);
258 RefCount = ChannelDereferenceByIndex(Index);
259 ASSERT(RefCount > 0);
260
261 /* Do we only have the keep-alive left, and the channel is dead? */
262 if ((RefCount == 1) && !(ChannelIsActive(Channel)))
263 {
264 /* Check if the ??? flag is set, or if there's no output data */
265 ThisChannel = ChannelFromIndex(Index);
266 if (!(ThisChannel->Flags & 1))
267 {
268 /* Nope, we can wipe the references and get rid of it */
269 ChannelDereferenceToZeroByIndex(Index);
270 }
271 else if (!ThisChannel->ChannelHasNewOBufferData)
272 {
273 /* No data, we can wipe the references and get rid of it */
274 ChannelDereferenceToZeroByIndex(Index);
275 }
276 }
277
278 /* We're done, we can unlock the slot now */
279 ChannelSlotUnlock(Index);
280 return STATUS_SUCCESS;
281 }
282
283 BOOLEAN
284 NTAPI
285 ChanMgrIsUniqueName(IN PWCHAR ChannelName)
286 {
287 NTSTATUS Status;
288 BOOLEAN IsUnique = FALSE;
289 PSAC_CHANNEL Channel;
290
291 /* Check if a channel with this name already exists */
292 Status = ChanMgrGetChannelByName(ChannelName, &Channel);
293 if (Status == STATUS_NOT_FOUND) IsUnique = TRUE;
294
295 /* If one did, dereference it, all we wanted was to check uniqueness */
296 if (NT_SUCCESS(Status)) ChanMgrReleaseChannel(Channel);
297
298 /* Return if one was found or not */
299 return IsUnique;
300 }
301
302 NTSTATUS
303 NTAPI
304 ChanMgrReapChannel(IN ULONG ChannelIndex)
305 {
306 /* FIXME: TODO */
307 return STATUS_NOT_IMPLEMENTED;
308 }
309
310 NTSTATUS
311 NTAPI
312 ChanMgrReapChannels(VOID)
313 {
314 ULONG i;
315 NTSTATUS Status = STATUS_SUCCESS;
316
317 /* Loop all the channels */
318 for (i = 0; i < SAC_MAX_CHANNELS; i++)
319 {
320 /* Lock this index and see if the channel was reaped */
321 ChannelSlotLock(i);
322 if (!ChannelReaped[i])
323 {
324 /* It was not reaped yet, so a channel should still be here */
325 ASSERT(ChannelFromIndex(i) != NULL);
326 if (ChannelGetReferenceCount(i) <= 0)
327 {
328 /* The channel has no more references, so clear the buffer flags */
329 _InterlockedExchange(&ChannelArray[i]->ChannelHasNewIBufferData, 0);
330 _InterlockedExchange(&ChannelArray[i]->ChannelHasNewOBufferData, 0);
331
332 /* And reap it */
333 Status = ChanMgrReapChannel(i);
334 }
335 }
336
337 /* Release the lock, and move on unless reaping failed */
338 ChannelSlotUnlock(i);
339 if (!NT_SUCCESS(Status)) break;
340 }
341
342 /* Return reaping status */
343 return Status;
344 }
345
346 NTSTATUS
347 NTAPI
348 ChanMgrCreateChannel(OUT PSAC_CHANNEL *Channel,
349 IN PSAC_CHANNEL_ATTRIBUTES Attributes)
350 {
351 NTSTATUS Status;
352 PSAC_CHANNEL NewChannel;
353 SAC_CHANNEL_ID ChanId;
354 ULONG i;
355 CHECK_PARAMETER(Channel);
356 CHECK_PARAMETER2(Attributes);
357
358 /* No other channel create attempts can happen */
359 ChannelLockCreates();
360
361 /* Is the channel manager initialized? */
362 if (ChannelCreateEnabled)
363 {
364 /* Nope, bail out */
365 Status = STATUS_UNSUCCESSFUL;
366 goto ReturnStatus;
367 }
368
369 /* Reap any zombie channels */
370 Status = ChanMgrReapChannels();
371 if (!NT_SUCCESS(Status))
372 {
373 /* Bail out on error */
374 Status = STATUS_UNSUCCESSFUL;
375 goto ReturnStatus;
376 }
377
378 /* Check if we already have a channel with this name */
379 if (!ChanMgrIsUniqueName(Attributes->NameBuffer))
380 {
381 /* We do, fail */
382 Status = STATUS_DUPLICATE_NAME;
383 goto ReturnStatus;
384 }
385
386 /* Allocate this channel */
387 NewChannel = SacAllocatePool(sizeof(SAC_CHANNEL), CHANNEL_BLOCK_TAG);
388 CHECK_PARAMETER_WITH_STATUS(NewChannel, STATUS_NO_MEMORY); // bug
389 RtlZeroMemory(NewChannel, sizeof(SAC_CHANNEL));
390
391 /* Loop channel slots */
392 for (i = 0; i < SAC_MAX_CHANNELS; i++)
393 {
394 /* Find a free spot for it */
395 if (ChannelReaped[i])
396 {
397 /* Free slot found, attempt to use it */
398 ASSERT(!CHANNEL_SLOT_IS_IN_USE(i));
399 _InterlockedCompareExchange((PLONG)&ChannelArray[i], (LONG)NewChannel, 0);
400 if (ChannelArray[i] == NewChannel) break;
401 }
402 }
403
404 /* Did we not find a single free slot? */
405 if (i == SAC_MAX_CHANNELS)
406 {
407 /* Bail out */
408 Status = STATUS_UNSUCCESSFUL;
409 goto ReturnStatus;
410 }
411
412 /* Create an ID for this channel */
413 RtlZeroMemory(&ChanId, sizeof(ChanId));
414 Status = ExUuidCreate(&ChanId.ChannelGuid);
415 if (!NT_SUCCESS(Status))
416 {
417 /* Bail out if we couldn't */
418 SAC_DBG(SAC_DBG_INIT, "SAC Create Channel :: Failed to get GUID\n");
419 goto ReturnStatus;
420 }
421
422 /* Now create the channel proper */
423 Status = ChannelCreate(NewChannel, Attributes, ChanId);
424 if (NT_SUCCESS(Status))
425 {
426 /* Set the channel index */
427 _InterlockedExchange(&NewChannel->Index, i);
428
429 /* Add the initial reference to the channel */
430 ChannelReferenceToOneByIndexWithLock(i);
431
432 /* Return it to the caller */
433 *Channel = NewChannel;
434
435 /* This slot is now occupied */
436 ASSERT(ChannelReaped[i] == 1);
437 _InterlockedExchange(&ChannelReaped[i], 0);
438 }
439 else
440 {
441 /* We couldn't create it, free the buffer */
442 SacFreePool(NewChannel);
443 }
444
445 ReturnStatus:
446 /* Return whatever the operation status was */
447 ChannelUnlockCreates();
448 return Status;
449 }
450
451 NTSTATUS
452 NTAPI
453 ChanMgrGetByHandleAndFileObject(IN SAC_CHANNEL_ID ChannelId,
454 IN PFILE_OBJECT FileObject,
455 OUT PSAC_CHANNEL* TargetChannel)
456 {
457 NTSTATUS Status;
458 PSAC_CHANNEL FoundChannel;
459
460 /* Lookup the channel by ID first */
461 Status = ChanMgrGetByHandle(ChannelId, &FoundChannel);
462 if (NT_SUCCESS(Status))
463 {
464 /* We found it, now check if the file object matches */
465 if (FoundChannel->FileObject == FileObject)
466 {
467 /* Yep, return success */
468 *TargetChannel = FoundChannel;
469 }
470 else
471 {
472 /* Nope, drop the reference on the channel */
473 ChanMgrReleaseChannel(FoundChannel);
474
475 /* And return failure */
476 *TargetChannel = NULL;
477 Status = STATUS_NOT_FOUND;
478 }
479 }
480
481 /* Return if we found it or not */
482 return Status;
483 }
484
485 NTSTATUS
486 NTAPI
487 ChanMgrGetChannelIndex(IN PSAC_CHANNEL Channel,
488 IN PLONG ChannelIndex)
489 {
490 CHECK_PARAMETER1(Channel);
491 CHECK_PARAMETER2(ChannelIndex);
492
493 /* Just return the index of the channel */
494 *ChannelIndex = ChannelGetIndex(Channel);
495 return STATUS_SUCCESS;
496 }
497
498 NTSTATUS
499 NTAPI
500 ChanMgrGetByIndex(IN LONG TargetIndex,
501 IN PSAC_CHANNEL* TargetChannel)
502 {
503 NTSTATUS Status;
504 CHECK_PARAMETER1(TargetIndex < SAC_MAX_CHANNELS);
505 CHECK_PARAMETER2(TargetChannel);
506
507 /* Assume failure */
508 *TargetChannel = NULL;
509 Status = STATUS_NOT_FOUND;
510
511 /* Reference this one and check if it's valid */
512 if (ChannelReferenceByIndexWithLock(TargetIndex) > 0)
513 {
514 /* We found it, return it (with a reference held) */
515 *TargetChannel = ChannelFromIndex(TargetIndex);
516 return STATUS_SUCCESS;
517 }
518
519 /* No channels with this ID were found */
520 return Status;
521 }
522
523 NTSTATUS
524 NTAPI
525 ChanMgrGetNextActiveChannel(IN PSAC_CHANNEL CurrentChannel,
526 IN PULONG TargetIndex,
527 OUT PSAC_CHANNEL *TargetChannel)
528 {
529 NTSTATUS Status;
530 ULONG i;
531 LONG ChannelIndex, StartIndex;
532 PSAC_CHANNEL FoundChannel;
533 BOOLEAN ChannelFound;
534 CHECK_PARAMETER1(CurrentChannel);
535 CHECK_PARAMETER2(TargetIndex);
536 CHECK_PARAMETER3(TargetChannel);
537
538 /* Get the current channel index */
539 Status = ChanMgrGetChannelIndex(CurrentChannel, &ChannelIndex);
540 if (!NT_SUCCESS(Status)) return Status;
541
542 /* Assume failure */
543 ChannelFound = FALSE;
544
545 /* Loop through all the possible active channels */
546 StartIndex = (ChannelIndex + 1) % SAC_MAX_CHANNELS;
547 for (i = StartIndex; i != StartIndex; i = (i + 1) % SAC_MAX_CHANNELS)
548 {
549 /* Get the channel and see if it exists*/
550 Status = ChanMgrGetByIndex(i, &FoundChannel);
551 if (Status != STATUS_NOT_FOUND)
552 {
553 /* Bail out if we failed for some reason */
554 if (!NT_SUCCESS(Status)) return Status;
555
556 /* It exists -- is it active? Or, does it have output data? */
557 if ((ChannelIsActive(FoundChannel)) ||
558 (!(ChannelIsActive(FoundChannel)) &&
559 (FoundChannel->ChannelHasNewOBufferData)))
560 {
561 /* It's active or has output data, return with it */
562 ChannelFound = TRUE;
563 break;
564 }
565
566 /* Drop the reference on this channel and try the next one */
567 Status = ChanMgrReleaseChannel(FoundChannel);
568 if (!NT_SUCCESS(Status)) return Status;
569 }
570 }
571
572 /* Check if we successfully found a channel */
573 if ((NT_SUCCESS(Status)) && (ChannelFound))
574 {
575 /* Return it and its indexed. Remember we still hold the reference */
576 *TargetIndex = i;
577 *TargetChannel = FoundChannel;
578 }
579
580 /* All done */
581 return Status;
582 }
583
584 NTSTATUS
585 NTAPI
586 ChanMgrChannelDestroy(IN PSAC_CHANNEL Channel)
587 {
588 CHECK_PARAMETER1(Channel);
589 CHECK_PARAMETER(ChannelGetReferenceCount(Channel->Index) > 0);
590
591 /* Destroy the channel */
592 return Channel->ChannelDestroy(Channel);
593 }
594
595 NTSTATUS
596 NTAPI
597 ChanMgrCloseChannel(IN PSAC_CHANNEL Channel)
598 {
599 NTSTATUS Status;
600 CHECK_PARAMETER(Channel);
601
602 /* Check if the channel is active */
603 if (ChannelIsActive(Channel))
604 {
605 /* Yep, close it */
606 Status = ChannelClose(Channel);
607 }
608 else
609 {
610 /* Nothing to do */
611 Status = STATUS_ALREADY_DISCONNECTED;
612 }
613
614 /* Handle the channel close */
615 ConMgrHandleEvent(TRUE, Channel, &Status);
616 return Status;
617 }
618
619 NTSTATUS
620 NTAPI
621 ChanMgrGetChannelCount(OUT PULONG ChannelCount)
622 {
623 ULONG i;
624 PSAC_CHANNEL Channel;
625 NTSTATUS Status;
626 CHECK_PARAMETER(ChannelCount);
627
628 /* Assume no channels */
629 *ChannelCount = 0;
630
631 /* Loop every channel */
632 for (i = 0; i < SAC_MAX_CHANNELS; i++)
633 {
634 /* See if this one exists */
635 Status = ChanMgrGetByIndex(i, &Channel);
636 if (Status != STATUS_NOT_FOUND)
637 {
638 /* Sanity checks*/
639 ASSERT(NT_SUCCESS(Status));
640 ASSERT(Channel != NULL);
641
642 /* It exists -- is it active? Or, does it have output data? */
643 if ((ChannelIsActive(Channel)) ||
644 (!(ChannelIsActive(Channel)) &&
645 (Channel->ChannelHasNewOBufferData)))
646 {
647 /* It's active or has output data, increase the count */
648 ++*ChannelCount;
649 break;
650 }
651
652 /* Drop the reference on this channel and try the next one */
653 Status = ChanMgrReleaseChannel(Channel);
654 if (!NT_SUCCESS(Status)) return Status;
655 }
656 else
657 {
658 /* Channel doesn't exist, nothing wrong with that, keep going */
659 Status = STATUS_SUCCESS;
660 }
661 }
662
663 /* We should always succeed if we get here */
664 ASSERT(NT_SUCCESS(Status));
665 return Status;
666 }
667
668 NTSTATUS
669 NTAPI
670 ChanMgrIsFull(OUT PBOOLEAN IsFull)
671 {
672 NTSTATUS Status;
673 ULONG Count;
674
675 /* Count the channels */
676 Status = ChanMgrGetChannelCount(&Count);
677 CHECK_PARAMETER(Status == STATUS_SUCCESS);
678
679 /* Return if we hit the limit */
680 *IsFull = (Count == SAC_MAX_CHANNELS);
681 return Status;
682 }
683
684 NTSTATUS
685 NTAPI
686 ChanMgrCloseChannelsWithFileObject(IN PFILE_OBJECT FileObject)
687 {
688 PSAC_CHANNEL Channel;
689 ULONG i;
690 NTSTATUS Status;
691 CHECK_PARAMETER1(FileObject);
692
693 /* Loop all channels */
694 for (i = 0; i < SAC_MAX_CHANNELS; i++)
695 {
696 /* Try to get this one */
697 Status = ChanMgrGetByIndex(i, &Channel);
698 if (!NT_SUCCESS(Status)) break;
699
700 /* Check if the FO matches, if so, close the channel */
701 if (Channel->FileObject == FileObject) ChanMgrCloseChannel(Channel);
702
703 /* Drop the reference and try the next channel(s) */
704 Status = ChanMgrReleaseChannel(Channel);
705 if (!NT_SUCCESS(Status)) break;
706 }
707
708 /* All done */
709 return Status;
710 }
711
712 NTSTATUS
713 NTAPI
714 ChanMgrGenerateUniqueCmdName(IN PWCHAR ChannelName)
715 {
716 return STATUS_NOT_IMPLEMENTED;
717 }