2  * Copyright (c) 1998-2004 Apple Computer, Inc. All rights reserved. 
   4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 
   6  * This file contains Original Code and/or Modifications of Original Code 
   7  * as defined in and that are subject to the Apple Public Source License 
   8  * Version 2.0 (the 'License'). You may not use this file except in 
   9  * compliance with the License. The rights granted to you under the License 
  10  * may not be used to create, or enable the creation or redistribution of, 
  11  * unlawful or unlicensed copies of an Apple operating system, or to 
  12  * circumvent, violate, or enable the circumvention or violation of, any 
  13  * terms of an Apple operating system software license agreement. 
  15  * Please obtain a copy of the License at 
  16  * http://www.opensource.apple.com/apsl/ and read it before using this file. 
  18  * The Original Code and all software distributed under the License are 
  19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 
  22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
  23  * Please see the License for the specific language governing rights and 
  24  * limitations under the License. 
  26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 
  29 Copyright (c) 1998 Apple Computer, Inc.  All rights reserved. 
  32     1998-7-13   Godfrey van der Linden(gvdl) 
  35 #include <IOKit/IOCommandQueue.h> 
  36 #include <IOKit/IOWorkLoop.h> 
  37 #include <IOKit/IOTimeStamp.h> 
  39 #include <mach/sync_policy.h> 
  41 #define NUM_FIELDS_IN_COMMAND   4 
  42 typedef struct commandEntryTag 
{ 
  43     void *f
[NUM_FIELDS_IN_COMMAND
]; 
  46 #define super IOEventSource 
  48 OSDefineMetaClassAndStructors(IOCommandQueue
, IOEventSource
) 
  53 initWithNext:owner:action:size: 
  54     - initWithNext: (IOEventSource *) inNext 
  56             action: (SEL) inAction 
  59 Primary initialiser for the IOCommandQueue class.  Returns an 
  60 IOCommandQueue object that is initialised with the next object in 
  61 the chain and the owner and action. On return the signalWorkAvailableIMP 
  62 has been cached for this function. 
  64 If the object fails to initialise for some reason then [self free] will 
  65 be called and nil will be returned. 
  67 See also: initWithNext:owner:action:(IOEventSource) 
  69 bool IOCommandQueue::init(OSObject 
*inOwner
, 
  70                           IOCommandQueueAction inAction
, 
  73     if ( !super::init(inOwner
, (IOEventSourceAction
) inAction
) ) 
  77     !=  semaphore_create(kernel_task
, &producerSema
, SYNC_POLICY_FIFO
, inSize
)) 
  80     size 
= inSize 
+ 1; /* Allocate one more entry than needed */ 
  82     queue 
= (void *)kalloc(size 
* sizeof(commandEntryT
)); 
  86     producerLock 
= IOLockAlloc(); 
  90     producerIndex 
= consumerIndex 
= 0; 
  96 IOCommandQueue::commandQueue(OSObject 
*inOwner
, 
  97                              IOCommandQueueAction inAction
, 
 100     IOCommandQueue 
*me 
= new IOCommandQueue
; 
 102     if (me 
&& !me
->init(inOwner
, inAction
, inSize
)) { 
 114 Mandatory free of the object independent of the current retain count. 
 117 void IOCommandQueue::free() 
 120         kfree(queue
, size 
* sizeof(commandEntryT
)); 
 122         semaphore_destroy(kernel_task
, producerSema
); 
 124         IOLockFree(producerLock
); 
 129 #if NUM_FIELDS_IN_COMMAND != 4 
 130 #error IOCommandQueue::checkForWork needs to be updated for new command size 
 133 bool IOCommandQueue::checkForWork() 
 135     void *field0
, *field1
, *field2
, *field3
; 
 137     if (!enabled 
|| consumerIndex 
== producerIndex
) 
 141         commandEntryT 
*q 
= (commandEntryT 
*) queue
; 
 142         int localIndex 
= consumerIndex
; 
 144         field0 
= q
[localIndex
].f
[0]; field1 
= q
[localIndex
].f
[1]; 
 145         field2 
= q
[localIndex
].f
[2]; field3 
= q
[localIndex
].f
[3]; 
 146         semaphore_signal(producerSema
); 
 149     if (++consumerIndex 
>= size
) 
 152     IOTimeStampConstant(IODBG_CMDQ(IOCMDQ_ACTION
), 
 153                         (unsigned int) action
, (unsigned int) owner
); 
 155     (*(IOCommandQueueAction
) action
)(owner
, field0
, field1
, field2
, field3
); 
 157     return (consumerIndex 
!= producerIndex
); 
 161 enqueueSleep:command: 
 162     - (kern_return_t) enqueueSleepRaw: (BOOL) gotoSleep 
 163                                field0: (void *) field0 field1: (void *) field1 
 164                                field2: (void *) field2 field3: (void *) field3; 
 166 Key method that enqueues the four input fields onto the command queue 
 167 and calls signalWorkAvailable to indicate that work is available to the 
 168 consumer.  This routine is safe against multiple threaded producers. 
 170 A family of convenience functions have been provided to assist with the 
 171 enqueueing of an method selector and an integer tag.  This relies on the 
 172 IODevice rawCommandOccurred... command to forward on the requests. 
 174 See also: signalWorkAvailable, checkForWork 
 176 #if NUM_FIELDS_IN_COMMAND != 4 
 177 #error IOCommandQueue::enqueueCommand needs to be updated 
 181 IOCommandQueue::enqueueCommand(bool gotoSleep
, 
 182                                void *field0
, void *field1
, 
 183                                void *field2
, void *field3
) 
 185     kern_return_t rtn 
= KERN_SUCCESS
; 
 188     /* Make sure there is room in the queue before doing anything else */ 
 193         rtn 
= semaphore_wait(producerSema
); 
 194         while(     (KERN_SUCCESS 
!= rtn
) 
 195                 && (KERN_OPERATION_TIMED_OUT 
!= rtn
) 
 196                 && (KERN_SEMAPHORE_DESTROYED 
!= rtn
) 
 197                 && (KERN_TERMINATED 
!= rtn
) 
 200         rtn 
= semaphore_timedwait(producerSema
, MACH_TIMESPEC_ZERO
); 
 202     if (KERN_SUCCESS 
!= rtn
) 
 205     /* Block other producers */ 
 206     IOTakeLock(producerLock
); 
 209      * Make sure that we update the current producer entry before we 
 210      * increment the producer pointer.  This avoids a nasty race as the 
 211      * as the test for work is producerIndex != consumerIndex and a signal. 
 214         commandEntryT 
*q 
= (commandEntryT 
*) queue
; 
 215         int localIndex 
= producerIndex
; 
 217         q
[localIndex
].f
[0] = field0
; q
[localIndex
].f
[1] = field1
; 
 218         q
[localIndex
].f
[2] = field2
; q
[localIndex
].f
[3] = field3
; 
 220     if (++producerIndex 
>= size
) 
 223     /* Clear to allow other producers to go now */ 
 224     IOUnlock(producerLock
); 
 227      * Right we have created some new work, we had better make sure that 
 228      * we notify the work loop that it has to test producerIndex. 
 230     signalWorkAvailable(); 
 234 int IOCommandQueue::performAndFlush(OSObject 
*target
, 
 235                                     IOCommandQueueAction inAction
) 
 240     // Set the defaults if necessary 
 244         inAction 
= (IOCommandQueueAction
) action
; 
 246     // Lock out the producers first 
 248         rtn 
= semaphore_timedwait(producerSema
, MACH_TIMESPEC_ZERO
); 
 249     } while (rtn 
== KERN_SUCCESS
); 
 251     // now step over all remaining entries in the command queue 
 252     for (numEntries 
= 0; consumerIndex 
!= producerIndex
; ) { 
 253         void *field0
, *field1
, *field2
, *field3
; 
 256             commandEntryT 
*q 
= (commandEntryT 
*) queue
; 
 257             int localIndex 
= consumerIndex
; 
 259             field0 
= q
[localIndex
].f
[0]; field1 
= q
[localIndex
].f
[1]; 
 260             field2 
= q
[localIndex
].f
[2]; field3 
= q
[localIndex
].f
[3]; 
 263         if (++consumerIndex 
>= size
) 
 266         (*inAction
)(target
, field0
, field1
, field2
, field3
); 
 269     // finally refill the producer semaphore to size - 1 
 270     for (int i 
= 1; i 
< size
; i
++) 
 271         semaphore_signal(producerSema
);