]> git.saurik.com Git - apple/xnu.git/blame - iokit/Kernel/IODataQueue.cpp
xnu-344.21.74.tar.gz
[apple/xnu.git] / iokit / Kernel / IODataQueue.cpp
CommitLineData
1c79356b
A
1/*
2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
d7e50217 6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
1c79356b 7 *
d7e50217
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,
d7e50217
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
26#include <IOKit/IODataQueue.h>
27#include <IOKit/IODataQueueShared.h>
28#include <IOKit/IOLib.h>
29#include <IOKit/IOMemoryDescriptor.h>
30
31#ifdef enqueue
32#undef enqueue
33#endif
34
35#ifdef dequeue
36#undef dequeue
37#endif
38
39#define super OSObject
40
41OSDefineMetaClassAndStructors(IODataQueue, OSObject)
42
43IODataQueue *IODataQueue::withCapacity(UInt32 size)
44{
45 IODataQueue *dataQueue = new IODataQueue;
46
47 if (dataQueue) {
48 if (!dataQueue->initWithCapacity(size)) {
49 dataQueue->release();
50 dataQueue = 0;
51 }
52 }
53
54 return dataQueue;
55}
56
57IODataQueue *IODataQueue::withEntries(UInt32 numEntries, UInt32 entrySize)
58{
59 IODataQueue *dataQueue = new IODataQueue;
60
61 if (dataQueue) {
62 if (!dataQueue->initWithEntries(numEntries, entrySize)) {
63 dataQueue->release();
64 dataQueue = 0;
65 }
66 }
67
68 return dataQueue;
69}
70
71Boolean IODataQueue::initWithCapacity(UInt32 size)
72{
73 if (!super::init()) {
74 return false;
75 }
76
d7e50217 77 dataQueue = (IODataQueueMemory *)IOMallocAligned(round_page_32(size + DATA_QUEUE_MEMORY_HEADER_SIZE), PAGE_SIZE);
1c79356b
A
78 if (dataQueue == 0) {
79 return false;
80 }
81
82 dataQueue->queueSize = size;
83 dataQueue->head = 0;
84 dataQueue->tail = 0;
85
86 return true;
87}
88
89Boolean IODataQueue::initWithEntries(UInt32 numEntries, UInt32 entrySize)
90{
91 return (initWithCapacity((numEntries + 1) * (DATA_QUEUE_ENTRY_HEADER_SIZE + entrySize)));
92}
93
94void IODataQueue::free()
95{
96 if (dataQueue) {
d7e50217 97 IOFreeAligned(dataQueue, round_page_32(dataQueue->queueSize + DATA_QUEUE_MEMORY_HEADER_SIZE));
1c79356b
A
98 }
99
100 super::free();
101
102 return;
103}
104
105Boolean IODataQueue::enqueue(void * data, UInt32 dataSize)
106{
107 const UInt32 head = dataQueue->head; // volatile
108 const UInt32 tail = dataQueue->tail;
109 const UInt32 entrySize = dataSize + DATA_QUEUE_ENTRY_HEADER_SIZE;
110 IODataQueueEntry * entry;
9bccf70c 111
1c79356b
A
112 if ( tail >= head )
113 {
9bccf70c
A
114 // Is there enough room at the end for the entry?
115 if ( (tail + entrySize) <= dataQueue->queueSize )
1c79356b
A
116 {
117 entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail);
118
119 entry->size = dataSize;
120 memcpy(&entry->data, data, dataSize);
9bccf70c
A
121
122 // The tail can be out of bound when the size of the new entry
123 // exactly matches the available space at the end of the queue.
124 // The tail can range from 0 to dataQueue->queueSize inclusive.
125
1c79356b
A
126 dataQueue->tail += entrySize;
127 }
9bccf70c 128 else if ( head > entrySize ) // Is there enough room at the beginning?
1c79356b
A
129 {
130 // Wrap around to the beginning, but do not allow the tail to catch
131 // up to the head.
132
133 dataQueue->queue->size = dataSize;
9bccf70c
A
134
135 // We need to make sure that there is enough room to set the size before
136 // doing this. The user client checks for this and will look for the size
137 // at the beginning if there isn't room for it at the end.
138
139 if ( ( dataQueue->queueSize - tail ) >= DATA_QUEUE_ENTRY_HEADER_SIZE )
140 {
141 ((IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail))->size = dataSize;
142 }
143
1c79356b
A
144 memcpy(&dataQueue->queue->data, data, dataSize);
145 dataQueue->tail = entrySize;
146 }
147 else
148 {
149 return false; // queue is full
150 }
151 }
152 else
153 {
154 // Do not allow the tail to catch up to the head when the queue is full.
155 // That's why the comparison uses a '>' rather than '>='.
156
157 if ( (head - tail) > entrySize )
158 {
159 entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail);
160
161 entry->size = dataSize;
162 memcpy(&entry->data, data, dataSize);
163 dataQueue->tail += entrySize;
164 }
165 else
166 {
167 return false; // queue is full
168 }
169 }
170
171 // Send notification (via mach message) that data is available.
172
173 if ( ( head == tail ) /* queue was empty prior to enqueue() */
174 || ( dataQueue->head == tail ) ) /* queue was emptied during enqueue() */
175 {
176 sendDataAvailableNotification();
177 }
178
179 return true;
180}
181
182void IODataQueue::setNotificationPort(mach_port_t port)
183{
184 static struct _notifyMsg init_msg = { {
185 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0),
186 sizeof (struct _notifyMsg),
187 MACH_PORT_NULL,
188 MACH_PORT_NULL,
189 0,
190 0
191 } };
192
193 if (notifyMsg == 0) {
194 notifyMsg = IOMalloc(sizeof(struct _notifyMsg));
195 }
196
197 *((struct _notifyMsg *)notifyMsg) = init_msg;
198
199 ((struct _notifyMsg *)notifyMsg)->h.msgh_remote_port = port;
200}
201
202void IODataQueue::sendDataAvailableNotification()
203{
204 kern_return_t kr;
205 mach_msg_header_t * msgh;
206
207 msgh = (mach_msg_header_t *)notifyMsg;
208 if (msgh) {
209 kr = mach_msg_send_from_kernel(msgh, msgh->msgh_size);
210 switch(kr) {
211 case MACH_SEND_TIMED_OUT: // Notification already sent
212 case MACH_MSG_SUCCESS:
213 break;
214 default:
215 IOLog("%s: dataAvailableNotification failed - msg_send returned: %d\n", /*getName()*/"IODataQueue", kr);
216 break;
217 }
218 }
219}
220
221IOMemoryDescriptor *IODataQueue::getMemoryDescriptor()
222{
223 IOMemoryDescriptor *descriptor = 0;
224
225 if (dataQueue != 0) {
226 descriptor = IOMemoryDescriptor::withAddress(dataQueue, dataQueue->queueSize + DATA_QUEUE_MEMORY_HEADER_SIZE, kIODirectionOutIn);
227 }
228
229 return descriptor;
230}
231