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