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