2 * Copyright (c) 1998-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_OSREFERENCE_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
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
31 Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
34 1998-7-13 Godfrey van der Linden(gvdl)
37 #include <IOKit/IOCommandQueue.h>
38 #include <IOKit/IOWorkLoop.h>
39 #include <IOKit/IOTimeStamp.h>
41 #include <mach/sync_policy.h>
43 #define NUM_FIELDS_IN_COMMAND 4
44 typedef struct commandEntryTag
{
45 void *f
[NUM_FIELDS_IN_COMMAND
];
48 #define super IOEventSource
50 OSDefineMetaClassAndStructors(IOCommandQueue
, IOEventSource
)
55 initWithNext:owner:action:size:
56 - initWithNext: (IOEventSource *) inNext
58 action: (SEL) inAction
61 Primary initialiser for the IOCommandQueue class. Returns an
62 IOCommandQueue object that is initialised with the next object in
63 the chain and the owner and action. On return the signalWorkAvailableIMP
64 has been cached for this function.
66 If the object fails to initialise for some reason then [self free] will
67 be called and nil will be returned.
69 See also: initWithNext:owner:action:(IOEventSource)
71 bool IOCommandQueue::init(OSObject
*inOwner
,
72 IOCommandQueueAction inAction
,
75 if ( !super::init(inOwner
, (IOEventSourceAction
) inAction
) )
79 != semaphore_create(kernel_task
, &producerSema
, SYNC_POLICY_FIFO
, inSize
))
82 size
= inSize
+ 1; /* Allocate one more entry than needed */
84 queue
= (void *)kalloc(size
* sizeof(commandEntryT
));
88 producerLock
= IOLockAlloc();
92 producerIndex
= consumerIndex
= 0;
98 IOCommandQueue::commandQueue(OSObject
*inOwner
,
99 IOCommandQueueAction inAction
,
102 IOCommandQueue
*me
= new IOCommandQueue
;
104 if (me
&& !me
->init(inOwner
, inAction
, inSize
)) {
116 Mandatory free of the object independent of the current retain count.
119 void IOCommandQueue::free()
122 kfree(queue
, size
* sizeof(commandEntryT
));
124 semaphore_destroy(kernel_task
, producerSema
);
126 IOLockFree(producerLock
);
131 #if NUM_FIELDS_IN_COMMAND != 4
132 #error IOCommandQueue::checkForWork needs to be updated for new command size
135 bool IOCommandQueue::checkForWork()
137 void *field0
, *field1
, *field2
, *field3
;
139 if (!enabled
|| consumerIndex
== producerIndex
)
143 commandEntryT
*q
= (commandEntryT
*) queue
;
144 int localIndex
= consumerIndex
;
146 field0
= q
[localIndex
].f
[0]; field1
= q
[localIndex
].f
[1];
147 field2
= q
[localIndex
].f
[2]; field3
= q
[localIndex
].f
[3];
148 semaphore_signal(producerSema
);
151 if (++consumerIndex
>= size
)
154 IOTimeStampConstant(IODBG_CMDQ(IOCMDQ_ACTION
),
155 (unsigned int) action
, (unsigned int) owner
);
157 (*(IOCommandQueueAction
) action
)(owner
, field0
, field1
, field2
, field3
);
159 return (consumerIndex
!= producerIndex
);
163 enqueueSleep:command:
164 - (kern_return_t) enqueueSleepRaw: (BOOL) gotoSleep
165 field0: (void *) field0 field1: (void *) field1
166 field2: (void *) field2 field3: (void *) field3;
168 Key method that enqueues the four input fields onto the command queue
169 and calls signalWorkAvailable to indicate that work is available to the
170 consumer. This routine is safe against multiple threaded producers.
172 A family of convenience functions have been provided to assist with the
173 enqueueing of an method selector and an integer tag. This relies on the
174 IODevice rawCommandOccurred... command to forward on the requests.
176 See also: signalWorkAvailable, checkForWork
178 #if NUM_FIELDS_IN_COMMAND != 4
179 #error IOCommandQueue::enqueueCommand needs to be updated
183 IOCommandQueue::enqueueCommand(bool gotoSleep
,
184 void *field0
, void *field1
,
185 void *field2
, void *field3
)
187 kern_return_t rtn
= KERN_SUCCESS
;
190 /* Make sure there is room in the queue before doing anything else */
195 rtn
= semaphore_wait(producerSema
);
196 while( (KERN_SUCCESS
!= rtn
)
197 && (KERN_OPERATION_TIMED_OUT
!= rtn
)
198 && (KERN_SEMAPHORE_DESTROYED
!= rtn
)
199 && (KERN_TERMINATED
!= rtn
)
202 rtn
= semaphore_timedwait(producerSema
, MACH_TIMESPEC_ZERO
);
204 if (KERN_SUCCESS
!= rtn
)
207 /* Block other producers */
208 IOTakeLock(producerLock
);
211 * Make sure that we update the current producer entry before we
212 * increment the producer pointer. This avoids a nasty race as the
213 * as the test for work is producerIndex != consumerIndex and a signal.
216 commandEntryT
*q
= (commandEntryT
*) queue
;
217 int localIndex
= producerIndex
;
219 q
[localIndex
].f
[0] = field0
; q
[localIndex
].f
[1] = field1
;
220 q
[localIndex
].f
[2] = field2
; q
[localIndex
].f
[3] = field3
;
222 if (++producerIndex
>= size
)
225 /* Clear to allow other producers to go now */
226 IOUnlock(producerLock
);
229 * Right we have created some new work, we had better make sure that
230 * we notify the work loop that it has to test producerIndex.
232 signalWorkAvailable();
236 int IOCommandQueue::performAndFlush(OSObject
*target
,
237 IOCommandQueueAction inAction
)
242 // Set the defaults if necessary
246 inAction
= (IOCommandQueueAction
) action
;
248 // Lock out the producers first
250 rtn
= semaphore_timedwait(producerSema
, MACH_TIMESPEC_ZERO
);
251 } while (rtn
== KERN_SUCCESS
);
253 // now step over all remaining entries in the command queue
254 for (numEntries
= 0; consumerIndex
!= producerIndex
; ) {
255 void *field0
, *field1
, *field2
, *field3
;
258 commandEntryT
*q
= (commandEntryT
*) queue
;
259 int localIndex
= consumerIndex
;
261 field0
= q
[localIndex
].f
[0]; field1
= q
[localIndex
].f
[1];
262 field2
= q
[localIndex
].f
[2]; field3
= q
[localIndex
].f
[3];
265 if (++consumerIndex
>= size
)
268 (*inAction
)(target
, field0
, field1
, field2
, field3
);
271 // finally refill the producer semaphore to size - 1
272 for (int i
= 1; i
< size
; i
++)
273 semaphore_signal(producerSema
);