2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
26 1998-7-13 Godfrey van der Linden(gvdl)
29 #include <IOKit/IOCommandQueue.h>
30 #include <IOKit/IOWorkLoop.h>
31 #include <IOKit/IOTimeStamp.h>
33 #include <mach/sync_policy.h>
35 #define NUM_FIELDS_IN_COMMAND 4
36 typedef struct commandEntryTag
{
37 void *f
[NUM_FIELDS_IN_COMMAND
];
40 #define super IOEventSource
42 OSDefineMetaClassAndStructors(IOCommandQueue
, IOEventSource
)
47 initWithNext:owner:action:size:
48 - initWithNext: (IOEventSource *) inNext
50 action: (SEL) inAction
53 Primary initialiser for the IOCommandQueue class. Returns an
54 IOCommandQueue object that is initialised with the next object in
55 the chain and the owner and action. On return the signalWorkAvailableIMP
56 has been cached for this function.
58 If the object fails to initialise for some reason then [self free] will
59 be called and nil will be returned.
61 See also: initWithNext:owner:action:(IOEventSource)
63 bool IOCommandQueue::init(OSObject
*inOwner
,
64 IOCommandQueueAction inAction
,
67 if ( !super::init(inOwner
, (IOEventSourceAction
) inAction
) )
71 != semaphore_create(kernel_task
, &producerSema
, SYNC_POLICY_FIFO
, inSize
))
74 size
= inSize
+ 1; /* Allocate one more entry than needed */
76 queue
= (void *)kalloc(size
* sizeof(commandEntryT
));
80 producerLock
= IOLockAlloc();
84 producerIndex
= consumerIndex
= 0;
90 IOCommandQueue::commandQueue(OSObject
*inOwner
,
91 IOCommandQueueAction inAction
,
94 IOCommandQueue
*me
= new IOCommandQueue
;
96 if (me
&& !me
->init(inOwner
, inAction
, inSize
)) {
108 Mandatory free of the object independent of the current retain count.
111 void IOCommandQueue::free()
114 kfree((vm_offset_t
)queue
, size
* sizeof(commandEntryT
));
116 semaphore_destroy(kernel_task
, producerSema
);
118 IOLockFree(producerLock
);
123 #if NUM_FIELDS_IN_COMMAND != 4
124 #error IOCommandQueue::checkForWork needs to be updated for new command size
127 bool IOCommandQueue::checkForWork()
129 void *field0
, *field1
, *field2
, *field3
;
131 if (!enabled
|| consumerIndex
== producerIndex
)
135 commandEntryT
*q
= (commandEntryT
*) queue
;
136 int localIndex
= consumerIndex
;
138 field0
= q
[localIndex
].f
[0]; field1
= q
[localIndex
].f
[1];
139 field2
= q
[localIndex
].f
[2]; field3
= q
[localIndex
].f
[3];
140 semaphore_signal(producerSema
);
143 if (++consumerIndex
>= size
)
146 IOTimeStampConstant(IODBG_CMDQ(IOCMDQ_ACTION
),
147 (unsigned int) action
, (unsigned int) owner
);
149 (*(IOCommandQueueAction
) action
)(owner
, field0
, field1
, field2
, field3
);
151 return (consumerIndex
!= producerIndex
);
155 enqueueSleep:command:
156 - (kern_return_t) enqueueSleepRaw: (BOOL) gotoSleep
157 field0: (void *) field0 field1: (void *) field1
158 field2: (void *) field2 field3: (void *) field3;
160 Key method that enqueues the four input fields onto the command queue
161 and calls signalWorkAvailable to indicate that work is available to the
162 consumer. This routine is safe against multiple threaded producers.
164 A family of convenience functions have been provided to assist with the
165 enqueueing of an method selector and an integer tag. This relies on the
166 IODevice rawCommandOccurred... command to forward on the requests.
168 See also: signalWorkAvailable, checkForWork
170 #if NUM_FIELDS_IN_COMMAND != 4
171 #error IOCommandQueue::enqueueCommand needs to be updated
175 IOCommandQueue::enqueueCommand(bool gotoSleep
,
176 void *field0
, void *field1
,
177 void *field2
, void *field3
)
179 kern_return_t rtn
= KERN_SUCCESS
;
182 /* Make sure there is room in the queue before doing anything else */
187 rtn
= semaphore_wait(producerSema
);
188 while( (KERN_SUCCESS
!= rtn
)
189 && (KERN_OPERATION_TIMED_OUT
!= rtn
)
190 && (KERN_SEMAPHORE_DESTROYED
!= rtn
)
191 && (KERN_TERMINATED
!= rtn
)
194 rtn
= semaphore_timedwait(producerSema
, MACH_TIMESPEC_ZERO
);
196 if (KERN_SUCCESS
!= rtn
)
199 /* Block other producers */
200 IOTakeLock(producerLock
);
203 * Make sure that we update the current producer entry before we
204 * increment the producer pointer. This avoids a nasty race as the
205 * as the test for work is producerIndex != consumerIndex and a signal.
208 commandEntryT
*q
= (commandEntryT
*) queue
;
209 int localIndex
= producerIndex
;
211 q
[localIndex
].f
[0] = field0
; q
[localIndex
].f
[1] = field1
;
212 q
[localIndex
].f
[2] = field2
; q
[localIndex
].f
[3] = field3
;
214 if (++producerIndex
>= size
)
217 /* Clear to allow other producers to go now */
218 IOUnlock(producerLock
);
221 * Right we have created some new work, we had better make sure that
222 * we notify the work loop that it has to test producerIndex.
224 signalWorkAvailable();
228 int IOCommandQueue::performAndFlush(OSObject
*target
,
229 IOCommandQueueAction inAction
)
234 // Set the defaults if necessary
238 inAction
= (IOCommandQueueAction
) action
;
240 // Lock out the producers first
242 rtn
= semaphore_timedwait(producerSema
, MACH_TIMESPEC_ZERO
);
243 } while (rtn
== KERN_SUCCESS
);
245 // now step over all remaining entries in the command queue
246 for (numEntries
= 0; consumerIndex
!= producerIndex
; ) {
247 void *field0
, *field1
, *field2
, *field3
;
250 commandEntryT
*q
= (commandEntryT
*) queue
;
251 int localIndex
= consumerIndex
;
253 field0
= q
[localIndex
].f
[0]; field1
= q
[localIndex
].f
[1];
254 field2
= q
[localIndex
].f
[2]; field3
= q
[localIndex
].f
[3];
257 if (++consumerIndex
>= size
)
260 (*inAction
)(target
, field0
, field1
, field2
, field3
);
263 // finally refill the producer semaphore to size - 1
264 for (int i
= 1; i
< size
; i
++)
265 semaphore_signal(producerSema
);