2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
29 1998-7-13 Godfrey van der Linden(gvdl)
32 #include <IOKit/IOCommandQueue.h>
33 #include <IOKit/IOWorkLoop.h>
34 #include <IOKit/IOTimeStamp.h>
36 #include <mach/sync_policy.h>
38 #define NUM_FIELDS_IN_COMMAND 4
39 typedef struct commandEntryTag
{
40 void *f
[NUM_FIELDS_IN_COMMAND
];
43 #define super IOEventSource
45 OSDefineMetaClassAndStructors(IOCommandQueue
, IOEventSource
)
50 initWithNext:owner:action:size:
51 - initWithNext: (IOEventSource *) inNext
53 action: (SEL) inAction
56 Primary initialiser for the IOCommandQueue class. Returns an
57 IOCommandQueue object that is initialised with the next object in
58 the chain and the owner and action. On return the signalWorkAvailableIMP
59 has been cached for this function.
61 If the object fails to initialise for some reason then [self free] will
62 be called and nil will be returned.
64 See also: initWithNext:owner:action:(IOEventSource)
66 bool IOCommandQueue::init(OSObject
*inOwner
,
67 IOCommandQueueAction inAction
,
70 if ( !super::init(inOwner
, (IOEventSourceAction
) inAction
) )
74 != semaphore_create(kernel_task
, &producerSema
, SYNC_POLICY_FIFO
, inSize
))
77 size
= inSize
+ 1; /* Allocate one more entry than needed */
79 queue
= (void *)kalloc(size
* sizeof(commandEntryT
));
83 producerLock
= IOLockAlloc();
87 producerIndex
= consumerIndex
= 0;
93 IOCommandQueue::commandQueue(OSObject
*inOwner
,
94 IOCommandQueueAction inAction
,
97 IOCommandQueue
*me
= new IOCommandQueue
;
99 if (me
&& !me
->init(inOwner
, inAction
, inSize
)) {
111 Mandatory free of the object independent of the current retain count.
114 void IOCommandQueue::free()
117 kfree((vm_offset_t
)queue
, size
* sizeof(commandEntryT
));
119 semaphore_destroy(kernel_task
, producerSema
);
121 IOLockFree(producerLock
);
126 #if NUM_FIELDS_IN_COMMAND != 4
127 #error IOCommandQueue::checkForWork needs to be updated for new command size
130 bool IOCommandQueue::checkForWork()
132 void *field0
, *field1
, *field2
, *field3
;
134 if (!enabled
|| consumerIndex
== producerIndex
)
138 commandEntryT
*q
= (commandEntryT
*) queue
;
139 int localIndex
= consumerIndex
;
141 field0
= q
[localIndex
].f
[0]; field1
= q
[localIndex
].f
[1];
142 field2
= q
[localIndex
].f
[2]; field3
= q
[localIndex
].f
[3];
143 semaphore_signal(producerSema
);
146 if (++consumerIndex
>= size
)
149 IOTimeStampConstant(IODBG_CMDQ(IOCMDQ_ACTION
),
150 (unsigned int) action
, (unsigned int) owner
);
152 (*(IOCommandQueueAction
) action
)(owner
, field0
, field1
, field2
, field3
);
154 return (consumerIndex
!= producerIndex
);
158 enqueueSleep:command:
159 - (kern_return_t) enqueueSleepRaw: (BOOL) gotoSleep
160 field0: (void *) field0 field1: (void *) field1
161 field2: (void *) field2 field3: (void *) field3;
163 Key method that enqueues the four input fields onto the command queue
164 and calls signalWorkAvailable to indicate that work is available to the
165 consumer. This routine is safe against multiple threaded producers.
167 A family of convenience functions have been provided to assist with the
168 enqueueing of an method selector and an integer tag. This relies on the
169 IODevice rawCommandOccurred... command to forward on the requests.
171 See also: signalWorkAvailable, checkForWork
173 #if NUM_FIELDS_IN_COMMAND != 4
174 #error IOCommandQueue::enqueueCommand needs to be updated
178 IOCommandQueue::enqueueCommand(bool gotoSleep
,
179 void *field0
, void *field1
,
180 void *field2
, void *field3
)
182 kern_return_t rtn
= KERN_SUCCESS
;
185 /* Make sure there is room in the queue before doing anything else */
190 rtn
= semaphore_wait(producerSema
);
191 while( (KERN_SUCCESS
!= rtn
)
192 && (KERN_OPERATION_TIMED_OUT
!= rtn
)
193 && (KERN_SEMAPHORE_DESTROYED
!= rtn
)
194 && (KERN_TERMINATED
!= rtn
)
197 rtn
= semaphore_timedwait(producerSema
, MACH_TIMESPEC_ZERO
);
199 if (KERN_SUCCESS
!= rtn
)
202 /* Block other producers */
203 IOTakeLock(producerLock
);
206 * Make sure that we update the current producer entry before we
207 * increment the producer pointer. This avoids a nasty race as the
208 * as the test for work is producerIndex != consumerIndex and a signal.
211 commandEntryT
*q
= (commandEntryT
*) queue
;
212 int localIndex
= producerIndex
;
214 q
[localIndex
].f
[0] = field0
; q
[localIndex
].f
[1] = field1
;
215 q
[localIndex
].f
[2] = field2
; q
[localIndex
].f
[3] = field3
;
217 if (++producerIndex
>= size
)
220 /* Clear to allow other producers to go now */
221 IOUnlock(producerLock
);
224 * Right we have created some new work, we had better make sure that
225 * we notify the work loop that it has to test producerIndex.
227 signalWorkAvailable();
231 int IOCommandQueue::performAndFlush(OSObject
*target
,
232 IOCommandQueueAction inAction
)
237 // Set the defaults if necessary
241 inAction
= (IOCommandQueueAction
) action
;
243 // Lock out the producers first
245 rtn
= semaphore_timedwait(producerSema
, MACH_TIMESPEC_ZERO
);
246 } while (rtn
== KERN_SUCCESS
);
248 // now step over all remaining entries in the command queue
249 for (numEntries
= 0; consumerIndex
!= producerIndex
; ) {
250 void *field0
, *field1
, *field2
, *field3
;
253 commandEntryT
*q
= (commandEntryT
*) queue
;
254 int localIndex
= consumerIndex
;
256 field0
= q
[localIndex
].f
[0]; field1
= q
[localIndex
].f
[1];
257 field2
= q
[localIndex
].f
[2]; field3
= q
[localIndex
].f
[3];
260 if (++consumerIndex
>= size
)
263 (*inAction
)(target
, field0
, field1
, field2
, field3
);
266 // finally refill the producer semaphore to size - 1
267 for (int i
= 1; i
< size
; i
++)
268 semaphore_signal(producerSema
);