1/*
2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23#include <IOKit/IOLib.h>
24#include <IOKit/IOLocks.h>
25#include <IOKit/assert.h>
26#include <IOKit/IOKitKeys.h>
27
28#include <IOKit/graphics/IOAccelerator.h>
29#include <IOKit/graphics/IOGraphicsTypesPrivate.h>
30#include <IOKit/IOUserClient.h>
31
32
33OSDefineMetaClassAndStructors(IOAccelerator, IOService)
34
35/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
36
37static IOLock *         gLock;
38static queue_head_t     gGlobalList;
39static UInt32           gTotalCount;
40static SInt32           gTweak;
41
42struct IOAccelIDRecord
43{
44    IOAccelID           id;
45    SInt32              retain;
46    queue_chain_t       task_link;
47    queue_chain_t       glob_link;
48};
49
50enum { kTweakBits = 0x1f };     // sizeof(IOAccelIDRecord) == 24
51
52class IOAccelerationUserClient : public IOUserClient
53{
54    /*
55     * Declare the metaclass information that is used for runtime
56     * typechecking of IOKit objects.
57     */
58
59    OSDeclareDefaultStructors( IOAccelerationUserClient );
60
61private:
62    task_t              fTask;
63    queue_head_t        fTaskList;
64
65    static void initialize();
66
67public:
68    /* IOService overrides */
69    virtual bool start( IOService * provider );
70    virtual void stop( IOService * provider );
71
72    /* IOUserClient overrides */
73    virtual bool initWithTask( task_t owningTask, void * securityID,
74                                                UInt32 type,  OSDictionary * properties );
75    virtual IOReturn clientClose( void );
76
77    virtual IOExternalMethod * getTargetAndMethodForIndex(
78                                            IOService ** targetP, UInt32 index );
79
80
81    IOReturn extCreate(IOOptionBits options,
82                        IOAccelID requestedID, IOAccelID * idOut);
83    IOReturn extDestroy(IOOptionBits options, IOAccelID id);
84
85};
86
87#define super IOUserClient
88OSDefineMetaClassAndStructorsWithInit(IOAccelerationUserClient, IOUserClient,
89                                        IOAccelerationUserClient::initialize());
90
91/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
92
93void IOAccelerationUserClient::initialize()
94{
95    if (!gLock)
96    {
97        gLock = IOLockAlloc();
98        queue_init(&gGlobalList);
99    }
100}
101
102bool IOAccelerationUserClient::initWithTask( task_t owningTask, void * securityID,
103                                             UInt32 type,  OSDictionary * properties )
104{
105
106    if ( properties != NULL )
107            properties->setObject ( kIOUserClientCrossEndianCompatibleKey, kOSBooleanTrue );
108
109    fTask = owningTask;
110    queue_init(&fTaskList);
111
112    return( super::initWithTask( owningTask, securityID, type, properties ));
113}
114
115bool IOAccelerationUserClient::start( IOService * provider )
116{
117    if( !super::start( provider ))
118        return( false );
119
120    return (true);
121}
122
123IOReturn IOAccelerationUserClient::clientClose( void )
124{
125    if( !isInactive())
126        terminate();
127
128    return( kIOReturnSuccess );
129}
130
131void IOAccelerationUserClient::stop( IOService * provider )
132{
133    IOAccelIDRecord * record;
134
135    IOLockLock(gLock);
136
137    while (!queue_empty( &fTaskList ))
138    {
139        queue_remove_first( &fTaskList,
140                            record,
141                            IOAccelIDRecord *,
142                            task_link );
143
144        if (--record->retain)
145            record->task_link.next = 0;
146        else
147        {
148            queue_remove(&gGlobalList,
149                            record,
150                            IOAccelIDRecord *,
151                            glob_link);
152            gTotalCount--;
153            IODelete(record, IOAccelIDRecord, 1);
154        }
155    }
156    IOLockUnlock(gLock);
157
158    super::stop( provider );
159}
160
161IOExternalMethod * IOAccelerationUserClient::getTargetAndMethodForIndex(
162    IOService ** targetP, UInt32 index )
163{
164    static const IOExternalMethod methodTemplate[] =
165    {
166        /* 0 */  { NULL, (IOMethod) &IOAccelerationUserClient::extCreate,
167                    kIOUCScalarIScalarO, 2, 1 },
168        /* 1 */  { NULL, (IOMethod) &IOAccelerationUserClient::extDestroy,
169                    kIOUCScalarIScalarO, 2, 0 },
170    };
171
172    if (index > (sizeof(methodTemplate) / sizeof(methodTemplate[0])))
173        return (NULL);
174
175    *targetP = this;
176
177    return ((IOExternalMethod *)(methodTemplate + index));
178}
179
180/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
181
182static
183IOReturn _CreateID(queue_head_t * taskList, IOOptionBits options,
184                    IOAccelID requestedID, IOAccelID * idOut)
185{
186    IOReturn          err;
187    Boolean           found;
188    IOAccelIDRecord * record;
189    IOAccelIDRecord * dup;
190
191    record = IONew(IOAccelIDRecord, 1);
192    record->retain = 1;
193
194    IOLockLock(gLock);
195
196    gTotalCount++;
197
198    do
199    {
200        if (kIOAccelSpecificID & options)
201        {
202            if ((requestedID > 4095) || (requestedID < -4096))
203            {
204                err = kIOReturnExclusiveAccess;
205                break;
206            }
207
208            found = false;
209            queue_iterate(&gGlobalList,
210                            dup,
211                            IOAccelIDRecord *,
212                            glob_link)
213            {
214                found = (dup->id == requestedID);
215                if (found)
216                    break;
217            }
218
219            if (found)
220            {
221                err = kIOReturnExclusiveAccess;
222                break;
223            }
224
225            record->id = requestedID;
226        }
227        else
228        {
229            record->id = ((IOAccelID) (intptr_t) record) ^ (kTweakBits & gTweak++);
230        }
231
232        if (taskList)
233        {
234            queue_enter(taskList, record,
235                            IOAccelIDRecord *, task_link);
236        }
237        else
238            record->task_link.next = 0;
239
240        queue_enter(&gGlobalList, record,
241                        IOAccelIDRecord *, glob_link);
242
243        *idOut = record->id;
244        err = kIOReturnSuccess;
245    }
246    while (false);
247
248    if (kIOReturnSuccess != err)
249        gTotalCount--;
250
251    IOLockUnlock(gLock);
252
253    if (kIOReturnSuccess != err)
254    {
255        IODelete(record, IOAccelIDRecord, 1);
256    }
257    return (err);
258}
259
260IOReturn IOAccelerationUserClient::extCreate(IOOptionBits options,
261                                                IOAccelID requestedID, IOAccelID * idOut)
262{
263    return (_CreateID(&fTaskList, options, requestedID, idOut));
264}
265
266IOReturn IOAccelerationUserClient::extDestroy(IOOptionBits options, IOAccelID id)
267{
268    IOAccelIDRecord * record;
269    bool found = false;
270    IOLockLock(gLock);
271
272    queue_iterate(&fTaskList,
273                    record,
274                    IOAccelIDRecord *,
275                    task_link)
276    {
277        found = (record->id == id);
278        if (found)
279        {
280            queue_remove(&fTaskList,
281                            record,
282                            IOAccelIDRecord *,
283                            task_link);
284            if (--record->retain)
285                record->task_link.next = 0;
286            else
287            {
288                queue_remove(&gGlobalList,
289                                record,
290                                IOAccelIDRecord *,
291                                glob_link);
292                gTotalCount--;
293                IODelete(record, IOAccelIDRecord, 1);
294            }
295            break;
296        }
297    }
298
299    IOLockUnlock(gLock);
300
301    return (found ? kIOReturnSuccess : kIOReturnBadMessageID);
302}
303
304IOReturn
305IOAccelerator::createAccelID(IOOptionBits options, IOAccelID * identifier)
306{
307    return (_CreateID(0, options, *identifier, identifier));
308}
309
310IOReturn
311IOAccelerator::retainAccelID(IOOptionBits options, IOAccelID id)
312{
313    IOAccelIDRecord * record;
314    bool found = false;
315    IOLockLock(gLock);
316
317    queue_iterate(&gGlobalList,
318                    record,
319                    IOAccelIDRecord *,
320                    glob_link)
321    {
322        found = (record->id == id);
323        if (found)
324        {
325            record->retain++;
326            break;
327        }
328    }
329
330    IOLockUnlock(gLock);
331
332    return (found ? kIOReturnSuccess : kIOReturnBadMessageID);
333}
334
335IOReturn
336IOAccelerator::releaseAccelID(IOOptionBits options, IOAccelID id)
337{
338    IOAccelIDRecord * record;
339    bool found = false;
340    IOLockLock(gLock);
341
342    queue_iterate(&gGlobalList,
343                    record,
344                    IOAccelIDRecord *,
345                    glob_link)
346    {
347        found = (record->id == id);
348        if (found)
349        {
350            if (!--record->retain)
351            {
352                if (record->task_link.next)
353                    panic("IOAccelerator::releaseID task_link");
354
355                queue_remove(&gGlobalList,
356                                record,
357                                IOAccelIDRecord *,
358                                glob_link);
359                gTotalCount--;
360                IODelete(record, IOAccelIDRecord, 1);
361            }
362            break;
363        }
364    }
365
366    IOLockUnlock(gLock);
367
368    return (found ? kIOReturnSuccess : kIOReturnBadMessageID);
369}
370
371