2 * Copyright (c) 1998-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
27 1998-7-13 Godfrey van der Linden(gvdl)
30 #include <IOKit/IOCommandQueue.h>
31 #include <IOKit/IOWorkLoop.h>
32 #include <IOKit/IOTimeStamp.h>
34 #include <mach/sync_policy.h>
36 #define NUM_FIELDS_IN_COMMAND 4
37 typedef struct commandEntryTag
{
38 void *f
[NUM_FIELDS_IN_COMMAND
];
41 #define super IOEventSource
43 OSDefineMetaClassAndStructors(IOCommandQueue
, IOEventSource
)
48 initWithNext:owner:action:size:
49 - initWithNext: (IOEventSource *) inNext
51 action: (SEL) inAction
54 Primary initialiser for the IOCommandQueue class. Returns an
55 IOCommandQueue object that is initialised with the next object in
56 the chain and the owner and action. On return the signalWorkAvailableIMP
57 has been cached for this function.
59 If the object fails to initialise for some reason then [self free] will
60 be called and nil will be returned.
62 See also: initWithNext:owner:action:(IOEventSource)
64 bool IOCommandQueue::init(OSObject
*inOwner
,
65 IOCommandQueueAction inAction
,
68 if ( !super::init(inOwner
, (IOEventSourceAction
) inAction
) )
72 != semaphore_create(kernel_task
, &producerSema
, SYNC_POLICY_FIFO
, inSize
))
75 size
= inSize
+ 1; /* Allocate one more entry than needed */
77 queue
= (void *)kalloc(size
* sizeof(commandEntryT
));
81 producerLock
= IOLockAlloc();
85 producerIndex
= consumerIndex
= 0;
91 IOCommandQueue::commandQueue(OSObject
*inOwner
,
92 IOCommandQueueAction inAction
,
95 IOCommandQueue
*me
= new IOCommandQueue
;
97 if (me
&& !me
->init(inOwner
, inAction
, inSize
)) {
109 Mandatory free of the object independent of the current retain count.
112 void IOCommandQueue::free()
115 kfree(queue
, size
* sizeof(commandEntryT
));
117 semaphore_destroy(kernel_task
, producerSema
);
119 IOLockFree(producerLock
);
124 #if NUM_FIELDS_IN_COMMAND != 4
125 #error IOCommandQueue::checkForWork needs to be updated for new command size
128 bool IOCommandQueue::checkForWork()
130 void *field0
, *field1
, *field2
, *field3
;
132 if (!enabled
|| consumerIndex
== producerIndex
)
136 commandEntryT
*q
= (commandEntryT
*) queue
;
137 int localIndex
= consumerIndex
;
139 field0
= q
[localIndex
].f
[0]; field1
= q
[localIndex
].f
[1];
140 field2
= q
[localIndex
].f
[2]; field3
= q
[localIndex
].f
[3];
141 semaphore_signal(producerSema
);
144 if (++consumerIndex
>= size
)
147 IOTimeStampConstant(IODBG_CMDQ(IOCMDQ_ACTION
),
148 (unsigned int) action
, (unsigned int) owner
);
150 (*(IOCommandQueueAction
) action
)(owner
, field0
, field1
, field2
, field3
);
152 return (consumerIndex
!= producerIndex
);
156 enqueueSleep:command:
157 - (kern_return_t) enqueueSleepRaw: (BOOL) gotoSleep
158 field0: (void *) field0 field1: (void *) field1
159 field2: (void *) field2 field3: (void *) field3;
161 Key method that enqueues the four input fields onto the command queue
162 and calls signalWorkAvailable to indicate that work is available to the
163 consumer. This routine is safe against multiple threaded producers.
165 A family of convenience functions have been provided to assist with the
166 enqueueing of an method selector and an integer tag. This relies on the
167 IODevice rawCommandOccurred... command to forward on the requests.
169 See also: signalWorkAvailable, checkForWork
171 #if NUM_FIELDS_IN_COMMAND != 4
172 #error IOCommandQueue::enqueueCommand needs to be updated
176 IOCommandQueue::enqueueCommand(bool gotoSleep
,
177 void *field0
, void *field1
,
178 void *field2
, void *field3
)
180 kern_return_t rtn
= KERN_SUCCESS
;
183 /* Make sure there is room in the queue before doing anything else */
188 rtn
= semaphore_wait(producerSema
);
189 while( (KERN_SUCCESS
!= rtn
)
190 && (KERN_OPERATION_TIMED_OUT
!= rtn
)
191 && (KERN_SEMAPHORE_DESTROYED
!= rtn
)
192 && (KERN_TERMINATED
!= rtn
)
195 rtn
= semaphore_timedwait(producerSema
, MACH_TIMESPEC_ZERO
);
197 if (KERN_SUCCESS
!= rtn
)
200 /* Block other producers */
201 IOTakeLock(producerLock
);
204 * Make sure that we update the current producer entry before we
205 * increment the producer pointer. This avoids a nasty race as the
206 * as the test for work is producerIndex != consumerIndex and a signal.
209 commandEntryT
*q
= (commandEntryT
*) queue
;
210 int localIndex
= producerIndex
;
212 q
[localIndex
].f
[0] = field0
; q
[localIndex
].f
[1] = field1
;
213 q
[localIndex
].f
[2] = field2
; q
[localIndex
].f
[3] = field3
;
215 if (++producerIndex
>= size
)
218 /* Clear to allow other producers to go now */
219 IOUnlock(producerLock
);
222 * Right we have created some new work, we had better make sure that
223 * we notify the work loop that it has to test producerIndex.
225 signalWorkAvailable();
229 int IOCommandQueue::performAndFlush(OSObject
*target
,
230 IOCommandQueueAction inAction
)
235 // Set the defaults if necessary
239 inAction
= (IOCommandQueueAction
) action
;
241 // Lock out the producers first
243 rtn
= semaphore_timedwait(producerSema
, MACH_TIMESPEC_ZERO
);
244 } while (rtn
== KERN_SUCCESS
);
246 // now step over all remaining entries in the command queue
247 for (numEntries
= 0; consumerIndex
!= producerIndex
; ) {
248 void *field0
, *field1
, *field2
, *field3
;
251 commandEntryT
*q
= (commandEntryT
*) queue
;
252 int localIndex
= consumerIndex
;
254 field0
= q
[localIndex
].f
[0]; field1
= q
[localIndex
].f
[1];
255 field2
= q
[localIndex
].f
[2]; field3
= q
[localIndex
].f
[3];
258 if (++consumerIndex
>= size
)
261 (*inAction
)(target
, field0
, field1
, field2
, field3
);
264 // finally refill the producer semaphore to size - 1
265 for (int i
= 1; i
< size
; i
++)
266 semaphore_signal(producerSema
);