]> git.saurik.com Git - apple/xnu.git/blame - iokit/Kernel/IOCommandQueue.cpp
xnu-517.3.15.tar.gz
[apple/xnu.git] / iokit / Kernel / IOCommandQueue.cpp
CommitLineData
1c79356b
A
1/*
2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
43866e37 6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
1c79356b 7 *
43866e37
A
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
13 * file.
14 *
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
1c79356b
A
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
43866e37
A
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.
1c79356b
A
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25/*
26Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
27
28HISTORY
29 1998-7-13 Godfrey van der Linden(gvdl)
30 Created.
31]*/
32#include <IOKit/IOCommandQueue.h>
33#include <IOKit/IOWorkLoop.h>
34#include <IOKit/IOTimeStamp.h>
35
36#include <mach/sync_policy.h>
37
38#define NUM_FIELDS_IN_COMMAND 4
39typedef struct commandEntryTag {
40 void *f[NUM_FIELDS_IN_COMMAND];
41} commandEntryT;
42
43#define super IOEventSource
44
45OSDefineMetaClassAndStructors(IOCommandQueue, IOEventSource)
46
47/*[
48Instance Methods
49
50initWithNext:owner:action:size:
51 - initWithNext: (IOEventSource *) inNext
52 owner: (id) inOwner
53 action: (SEL) inAction
54 size: (int) inSize;
55
56Primary initialiser for the IOCommandQueue class. Returns an
57IOCommandQueue object that is initialised with the next object in
58the chain and the owner and action. On return the signalWorkAvailableIMP
59has been cached for this function.
60
61If the object fails to initialise for some reason then [self free] will
62be called and nil will be returned.
63
64See also: initWithNext:owner:action:(IOEventSource)
65]*/
66bool IOCommandQueue::init(OSObject *inOwner,
67 IOCommandQueueAction inAction,
68 int inSize)
69{
70 if ( !super::init(inOwner, (IOEventSourceAction) inAction) )
71 return false;
72
73 if (KERN_SUCCESS
74 != semaphore_create(kernel_task, &producerSema, SYNC_POLICY_FIFO, inSize))
75 return false;
76
77 size = inSize + 1; /* Allocate one more entry than needed */
78
79 queue = (void *)kalloc(size * sizeof(commandEntryT));
80 if (!queue)
81 return false;
82
83 producerLock = IOLockAlloc();
84 if (!producerLock)
85 return false;
86
87 producerIndex = consumerIndex = 0;
88
89 return true;
90}
91
92IOCommandQueue *
93IOCommandQueue::commandQueue(OSObject *inOwner,
94 IOCommandQueueAction inAction,
95 int inSize)
96{
97 IOCommandQueue *me = new IOCommandQueue;
98
99 if (me && !me->init(inOwner, inAction, inSize)) {
100 me->free();
101 return 0;
102 }
103
104 return me;
105}
106
107/*[
108free
109 - free
110
111Mandatory free of the object independent of the current retain count.
112Returns nil.
113]*/
114void IOCommandQueue::free()
115{
116 if (queue)
117 kfree((vm_offset_t)queue, size * sizeof(commandEntryT));
118 if (producerSema)
119 semaphore_destroy(kernel_task, producerSema);
120 if (producerLock)
121 IOLockFree(producerLock);
122
123 super::free();
124}
125
126#if NUM_FIELDS_IN_COMMAND != 4
127#error IOCommandQueue::checkForWork needs to be updated for new command size
128#endif
129
130bool IOCommandQueue::checkForWork()
131{
132 void *field0, *field1, *field2, *field3;
133
134 if (!enabled || consumerIndex == producerIndex)
135 return false;
136
137 {
138 commandEntryT *q = (commandEntryT *) queue;
139 int localIndex = consumerIndex;
140
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);
144 }
145
146 if (++consumerIndex >= size)
147 consumerIndex = 0;
148
149 IOTimeStampConstant(IODBG_CMDQ(IOCMDQ_ACTION),
150 (unsigned int) action, (unsigned int) owner);
151
152 (*(IOCommandQueueAction) action)(owner, field0, field1, field2, field3);
153
154 return (consumerIndex != producerIndex);
155}
156
157/*[
158enqueueSleep:command:
159 - (kern_return_t) enqueueSleepRaw: (BOOL) gotoSleep
160 field0: (void *) field0 field1: (void *) field1
161 field2: (void *) field2 field3: (void *) field3;
162
163Key method that enqueues the four input fields onto the command queue
164and calls signalWorkAvailable to indicate that work is available to the
165consumer. This routine is safe against multiple threaded producers.
166
167A family of convenience functions have been provided to assist with the
168enqueueing of an method selector and an integer tag. This relies on the
169IODevice rawCommandOccurred... command to forward on the requests.
170
171See also: signalWorkAvailable, checkForWork
172]*/
173#if NUM_FIELDS_IN_COMMAND != 4
174#error IOCommandQueue::enqueueCommand needs to be updated
175#endif
176
177kern_return_t
178IOCommandQueue::enqueueCommand(bool gotoSleep,
179 void *field0, void *field1,
180 void *field2, void *field3)
181{
182 kern_return_t rtn = KERN_SUCCESS;
183 int retry;
184
185 /* Make sure there is room in the queue before doing anything else */
186
187 if (gotoSleep) {
188 retry = 0;
189 do
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)
195 && ((retry++) < 4));
196 } else
197 rtn = semaphore_timedwait(producerSema, MACH_TIMESPEC_ZERO);
198
199 if (KERN_SUCCESS != rtn)
200 return rtn;
201
202 /* Block other producers */
203 IOTakeLock(producerLock);
204
205 /*
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.
209 */
210 {
211 commandEntryT *q = (commandEntryT *) queue;
212 int localIndex = producerIndex;
213
214 q[localIndex].f[0] = field0; q[localIndex].f[1] = field1;
215 q[localIndex].f[2] = field2; q[localIndex].f[3] = field3;
216 }
217 if (++producerIndex >= size)
218 producerIndex = 0;
219
220 /* Clear to allow other producers to go now */
221 IOUnlock(producerLock);
222
223 /*
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.
226 */
227 signalWorkAvailable();
228 return rtn;
229}
230
231int IOCommandQueue::performAndFlush(OSObject *target,
232 IOCommandQueueAction inAction)
233{
234 int numEntries;
235 kern_return_t rtn;
236
237 // Set the defaults if necessary
238 if (!target)
239 target = owner;
240 if (!inAction)
241 inAction = (IOCommandQueueAction) action;
242
243 // Lock out the producers first
244 do {
245 rtn = semaphore_timedwait(producerSema, MACH_TIMESPEC_ZERO);
246 } while (rtn == KERN_SUCCESS);
247
248 // now step over all remaining entries in the command queue
249 for (numEntries = 0; consumerIndex != producerIndex; ) {
250 void *field0, *field1, *field2, *field3;
251
252 {
253 commandEntryT *q = (commandEntryT *) queue;
254 int localIndex = consumerIndex;
255
256 field0 = q[localIndex].f[0]; field1 = q[localIndex].f[1];
257 field2 = q[localIndex].f[2]; field3 = q[localIndex].f[3];
258 }
259
260 if (++consumerIndex >= size)
261 consumerIndex = 0;
262
263 (*inAction)(target, field0, field1, field2, field3);
264 }
265
266 // finally refill the producer semaphore to size - 1
267 for (int i = 1; i < size; i++)
268 semaphore_signal(producerSema);
269
270 return numEntries;
271}