1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS Kernel Streaming 4 * FILE: drivers/wdm/audio/backpln/portcls/service_group.cpp 5 * PURPOSE: ServiceGroup object implementation 6 * PROGRAMMER: Johannes Anderwald 7 */ 8 9 #include "private.hpp" 10 11 #ifndef YDEBUG 12 #define NDEBUG 13 #endif 14 15 #include <debug.h> 16 17 VOID 18 NTAPI 19 IServiceGroupDpc( 20 IN struct _KDPC *Dpc, 21 IN PVOID DeferredContext, 22 IN PVOID SystemArgument1, 23 IN PVOID SystemArgument2 24 ); 25 26 typedef struct 27 { 28 LIST_ENTRY Entry; 29 IN PSERVICESINK pServiceSink; 30 }GROUP_ENTRY, *PGROUP_ENTRY; 31 32 class CServiceGroup : public CUnknownImpl<IServiceGroup> 33 { 34 public: 35 STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface); 36 37 IMP_IServiceGroup; 38 CServiceGroup(IUnknown * OuterUnknown); 39 virtual ~CServiceGroup() {} 40 41 protected: 42 43 LIST_ENTRY m_ServiceSinkHead; 44 45 BOOL m_TimerInitialized; 46 KTIMER m_Timer; 47 KDPC m_Dpc; 48 KSPIN_LOCK m_Lock; 49 50 friend VOID NTAPI IServiceGroupDpc(IN struct _KDPC *Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2); 51 }; 52 53 54 55 //--------------------------------------------------------------- 56 // IUnknown methods 57 // 58 59 60 NTSTATUS 61 NTAPI 62 CServiceGroup::QueryInterface( 63 IN REFIID refiid, 64 OUT PVOID* Output) 65 { 66 UNICODE_STRING GuidString; 67 68 if (IsEqualGUIDAligned(refiid, IID_IServiceGroup) || 69 IsEqualGUIDAligned(refiid, IID_IServiceSink) || 70 IsEqualGUIDAligned(refiid, IID_IUnknown)) 71 { 72 *Output = PVOID(PSERVICEGROUP(this)); 73 PUNKNOWN(*Output)->AddRef(); 74 return STATUS_SUCCESS; 75 } 76 77 if (RtlStringFromGUID(refiid, &GuidString) == STATUS_SUCCESS) 78 { 79 DPRINT1("CServiceGroup::QueryInterface no interface!!! iface %S\n", GuidString.Buffer); 80 RtlFreeUnicodeString(&GuidString); 81 } 82 83 return STATUS_UNSUCCESSFUL; 84 } 85 //--------------------------------------------------------------- 86 // IServiceSink methods 87 // 88 89 CServiceGroup::CServiceGroup(IUnknown * OuterUnknown) 90 { 91 // initialize dpc 92 KeInitializeDpc(&m_Dpc, IServiceGroupDpc, (PVOID)this); 93 94 // set highest importance 95 KeSetImportanceDpc(&m_Dpc, HighImportance); 96 97 // initialize service group list lock 98 KeInitializeSpinLock(&m_Lock); 99 100 // initialize service group list 101 InitializeListHead(&m_ServiceSinkHead); 102 } 103 104 VOID 105 NTAPI 106 CServiceGroup::RequestService() 107 { 108 KIRQL OldIrql; 109 110 DPRINT("CServiceGroup::RequestService() Dpc at Level %u\n", KeGetCurrentIrql()); 111 112 if (m_TimerInitialized) 113 { 114 LARGE_INTEGER DueTime; 115 116 // no due time 117 DueTime.QuadPart = 0LL; 118 119 // delayed service requested 120 KeSetTimer(&m_Timer, DueTime, &m_Dpc); 121 } 122 else 123 { 124 // check current irql 125 if (KeGetCurrentIrql() > DISPATCH_LEVEL) 126 { 127 //insert dpc to queue 128 KeInsertQueueDpc(&m_Dpc, NULL, NULL); 129 } 130 else 131 { 132 // raise irql to dispatch level to make dpc fire immediately 133 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); 134 // insert dpc to queue 135 KeInsertQueueDpc(&m_Dpc, NULL, NULL); 136 // lower irql to old level 137 KeLowerIrql(OldIrql); 138 } 139 } 140 } 141 142 //--------------------------------------------------------------- 143 // IServiceGroup methods 144 // 145 146 NTSTATUS 147 NTAPI 148 CServiceGroup::AddMember( 149 IN PSERVICESINK pServiceSink) 150 { 151 PGROUP_ENTRY Entry; 152 KIRQL OldLevel; 153 154 // sanity check 155 PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL); 156 157 // allocate service sink entry 158 Entry = (PGROUP_ENTRY)AllocateItem(NonPagedPool, sizeof(GROUP_ENTRY), TAG_PORTCLASS); 159 if (!Entry) 160 { 161 // out of memory 162 return STATUS_INSUFFICIENT_RESOURCES; 163 } 164 165 // initialize service sink entry 166 Entry->pServiceSink = pServiceSink; 167 // increment reference count 168 pServiceSink->AddRef(); 169 170 // acquire service group list lock 171 KeAcquireSpinLock(&m_Lock, &OldLevel); 172 173 // insert into service sink list 174 InsertTailList(&m_ServiceSinkHead, &Entry->Entry); 175 176 // release service group list lock 177 KeReleaseSpinLock(&m_Lock, OldLevel); 178 179 return STATUS_SUCCESS; 180 } 181 182 VOID 183 NTAPI 184 CServiceGroup::RemoveMember( 185 IN PSERVICESINK pServiceSink) 186 { 187 PLIST_ENTRY CurEntry; 188 PGROUP_ENTRY Entry; 189 KIRQL OldLevel; 190 191 // sanity check 192 PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL); 193 194 // acquire service group list lock 195 KeAcquireSpinLock(&m_Lock, &OldLevel); 196 197 // grab first entry 198 CurEntry = m_ServiceSinkHead.Flink; 199 200 // loop list until the passed entry is found 201 while (CurEntry != &m_ServiceSinkHead) 202 { 203 // grab entry 204 Entry = CONTAINING_RECORD(CurEntry, GROUP_ENTRY, Entry); 205 206 // check if it matches the passed entry 207 if (Entry->pServiceSink == pServiceSink) 208 { 209 // remove entry from list 210 RemoveEntryList(&Entry->Entry); 211 212 // release service sink reference 213 pServiceSink->Release(); 214 215 // free service sink entry 216 FreeItem(Entry, TAG_PORTCLASS); 217 218 // leave loop 219 break; 220 } 221 // move to next entry 222 CurEntry = CurEntry->Flink; 223 } 224 225 // release service group list lock 226 KeReleaseSpinLock(&m_Lock, OldLevel); 227 228 } 229 230 VOID 231 NTAPI 232 IServiceGroupDpc( 233 IN struct _KDPC *Dpc, 234 IN PVOID DeferredContext, 235 IN PVOID SystemArgument1, 236 IN PVOID SystemArgument2 237 ) 238 { 239 PLIST_ENTRY CurEntry; 240 PGROUP_ENTRY Entry; 241 CServiceGroup * This = (CServiceGroup*)DeferredContext; 242 243 // acquire service group list lock 244 KeAcquireSpinLockAtDpcLevel(&This->m_Lock); 245 246 // grab first entry 247 CurEntry = This->m_ServiceSinkHead.Flink; 248 249 // loop the list and call the attached service sink/group 250 while (CurEntry != &This->m_ServiceSinkHead) 251 { 252 //grab current entry 253 Entry = (PGROUP_ENTRY)CONTAINING_RECORD(CurEntry, GROUP_ENTRY, Entry); 254 255 // call service sink/group 256 Entry->pServiceSink->RequestService(); 257 258 // move to next entry 259 CurEntry = CurEntry->Flink; 260 } 261 262 // release service group list lock 263 KeReleaseSpinLockFromDpcLevel(&This->m_Lock); 264 } 265 266 VOID 267 NTAPI 268 CServiceGroup::SupportDelayedService() 269 { 270 PC_ASSERT_IRQL(DISPATCH_LEVEL); 271 272 // initialize the timer 273 KeInitializeTimer(&m_Timer); 274 275 // use the timer to perform service requests 276 m_TimerInitialized = TRUE; 277 } 278 279 VOID 280 NTAPI 281 CServiceGroup::RequestDelayedService( 282 IN ULONGLONG ullDelay) 283 { 284 LARGE_INTEGER DueTime; 285 286 // sanity check 287 PC_ASSERT_IRQL(DISPATCH_LEVEL); 288 PC_ASSERT(m_TimerInitialized); 289 290 DueTime.QuadPart = ullDelay; 291 292 // set the timer 293 KeSetTimer(&m_Timer, DueTime, &m_Dpc); 294 } 295 296 VOID 297 NTAPI 298 CServiceGroup::CancelDelayedService() 299 { 300 PC_ASSERT_IRQL(DISPATCH_LEVEL); 301 PC_ASSERT(m_TimerInitialized); 302 303 // cancel the timer 304 KeCancelTimer(&m_Timer); 305 } 306 307 NTSTATUS 308 NTAPI 309 PcNewServiceGroup( 310 OUT PSERVICEGROUP* OutServiceGroup, 311 IN PUNKNOWN OuterUnknown OPTIONAL) 312 { 313 CServiceGroup * This; 314 NTSTATUS Status; 315 316 DPRINT("PcNewServiceGroup entered\n"); 317 318 //FIXME support aggregation 319 PC_ASSERT(OuterUnknown == NULL); 320 321 // allocate a service group object 322 This = new(NonPagedPool, TAG_PORTCLASS)CServiceGroup(OuterUnknown); 323 324 if (!This) 325 { 326 // out of memory 327 return STATUS_INSUFFICIENT_RESOURCES; 328 } 329 330 // request IServiceSink interface 331 Status = This->QueryInterface(IID_IServiceSink, (PVOID*)OutServiceGroup); 332 333 if (!NT_SUCCESS(Status)) 334 { 335 // failed to acquire service sink interface 336 delete This; 337 return Status; 338 } 339 340 // done 341 return Status; 342 } 343