1/*
2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28#include <libkern/OSDebug.h>
29
30#include <IOKit/IOCommandGate.h>
31#include <IOKit/IOWorkLoop.h>
32#include <IOKit/IOReturn.h>
33#include <IOKit/IOTimeStamp.h>
34
35#define super IOEventSource
36
37OSDefineMetaClassAndStructors(IOCommandGate, IOEventSource)
38OSMetaClassDefineReservedUnused(IOCommandGate, 0);
39OSMetaClassDefineReservedUnused(IOCommandGate, 1);
40OSMetaClassDefineReservedUnused(IOCommandGate, 2);
41OSMetaClassDefineReservedUnused(IOCommandGate, 3);
42OSMetaClassDefineReservedUnused(IOCommandGate, 4);
43OSMetaClassDefineReservedUnused(IOCommandGate, 5);
44OSMetaClassDefineReservedUnused(IOCommandGate, 6);
45OSMetaClassDefineReservedUnused(IOCommandGate, 7);
46
47bool IOCommandGate::checkForWork() { return false; }
48
49bool IOCommandGate::init(OSObject *inOwner, Action inAction)
50{
51    return super::init(inOwner, (IOEventSource::Action) inAction);
52}
53
54IOCommandGate *
55IOCommandGate::commandGate(OSObject *inOwner, Action inAction)
56{
57    IOCommandGate *me = new IOCommandGate;
58
59    if (me && !me->init(inOwner, inAction)) {
60        me->release();
61        return 0;
62    }
63
64    return me;
65}
66
67/* virtual */ void IOCommandGate::disable()
68{
69    if (workLoop && !workLoop->inGate())
70	OSReportWithBacktrace("IOCommandGate::disable() called when not gated");
71
72    super::disable();
73}
74
75/* virtual */ void IOCommandGate::enable()
76{
77    if (workLoop) {
78	closeGate();
79	super::enable();
80	wakeupGate(&enabled, /* oneThread */ false); // Unblock sleeping threads
81	openGate();
82    }
83}
84
85/* virtual */ void IOCommandGate::free()
86{
87    setWorkLoop(0);
88    super::free();
89}
90
91/* virtual */ void IOCommandGate::setWorkLoop(IOWorkLoop *inWorkLoop)
92{
93    uintptr_t *sleepersP = (uintptr_t *) &reserved;
94    if (!inWorkLoop && workLoop) {		// tearing down
95	closeGate();
96	*sleepersP |= 1;
97	while (*sleepersP >> 1) {
98	    thread_wakeup_with_result(&enabled, THREAD_INTERRUPTED);
99	    sleepGate(sleepersP, THREAD_UNINT);
100	}
101	*sleepersP = 0;
102	openGate();
103    }
104    else
105
106    super::setWorkLoop(inWorkLoop);
107}
108
109IOReturn IOCommandGate::runCommand(void *arg0, void *arg1,
110                                   void *arg2, void *arg3)
111{
112    return runAction((Action) action, arg0, arg1, arg2, arg3);
113}
114
115IOReturn IOCommandGate::attemptCommand(void *arg0, void *arg1,
116                                       void *arg2, void *arg3)
117{
118    return attemptAction((Action) action, arg0, arg1, arg2, arg3);
119}
120
121IOReturn IOCommandGate::runAction(Action inAction,
122                                  void *arg0, void *arg1,
123                                  void *arg2, void *arg3)
124{
125    if (!inAction)
126        return kIOReturnBadArgument;
127
128    IOTimeStampConstant(IODBG_CMDQ(IOCMDQ_ACTION),
129			(unsigned int) inAction, (unsigned int) owner);
130
131    // closeGate is recursive needn't worry if we already hold the lock.
132    closeGate();
133
134    // If the command gate is disabled and we aren't on the workloop thread
135    // itself then sleep until we get enabled.
136    IOReturn res;
137    if (!workLoop->onThread()) {
138	while (!enabled) {
139	    uintptr_t *sleepersP = (uintptr_t *) &reserved;
140
141	    *sleepersP += 2;
142	    IOReturn res = sleepGate(&enabled, THREAD_ABORTSAFE);
143	    *sleepersP -= 2;
144
145	    bool wakeupTearDown = (*sleepersP & 1);
146	    if (res || wakeupTearDown) {
147		openGate();
148
149		 if (wakeupTearDown)
150		     commandWakeup(sleepersP);	// No further resources used
151
152		return kIOReturnAborted;
153	    }
154	}
155    }
156
157    // Must be gated and on the work loop or enabled
158    res = (*inAction)(owner, arg0, arg1, arg2, arg3);
159    openGate();
160
161    return res;
162}
163
164IOReturn IOCommandGate::attemptAction(Action inAction,
165                                      void *arg0, void *arg1,
166                                      void *arg2, void *arg3)
167{
168    IOReturn res;
169
170    if (!inAction)
171        return kIOReturnBadArgument;
172
173    // Try to close the gate if can't get return immediately.
174    if (!tryCloseGate())
175        return kIOReturnCannotLock;
176
177    // If the command gate is disabled then sleep until we get a wakeup
178    if (!workLoop->onThread() && !enabled)
179        res = kIOReturnNotPermitted;
180    else {
181	IOTimeStampConstant(IODBG_CMDQ(IOCMDQ_ACTION),
182			    (unsigned int) inAction, (unsigned int) owner);
183
184	res = (*inAction)(owner, arg0, arg1, arg2, arg3);
185    }
186
187    openGate();
188
189    return res;
190}
191
192IOReturn IOCommandGate::commandSleep(void *event, UInt32 interruptible)
193{
194    if (!workLoop->inGate())
195        return kIOReturnNotPermitted;
196
197    return sleepGate(event, interruptible);
198}
199
200void IOCommandGate::commandWakeup(void *event, bool oneThread)
201{
202    wakeupGate(event, oneThread);
203}
204