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
);