]> git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IODataQueue.cpp
xnu-344.21.74.tar.gz
[apple/xnu.git] / iokit / Kernel / IODataQueue.cpp
1 /*
2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
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
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
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.
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
41 OSDefineMetaClassAndStructors(IODataQueue, OSObject)
42
43 IODataQueue *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
57 IODataQueue *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
71 Boolean IODataQueue::initWithCapacity(UInt32 size)
72 {
73 if (!super::init()) {
74 return false;
75 }
76
77 dataQueue = (IODataQueueMemory *)IOMallocAligned(round_page_32(size + DATA_QUEUE_MEMORY_HEADER_SIZE), PAGE_SIZE);
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
89 Boolean IODataQueue::initWithEntries(UInt32 numEntries, UInt32 entrySize)
90 {
91 return (initWithCapacity((numEntries + 1) * (DATA_QUEUE_ENTRY_HEADER_SIZE + entrySize)));
92 }
93
94 void IODataQueue::free()
95 {
96 if (dataQueue) {
97 IOFreeAligned(dataQueue, round_page_32(dataQueue->queueSize + DATA_QUEUE_MEMORY_HEADER_SIZE));
98 }
99
100 super::free();
101
102 return;
103 }
104
105 Boolean 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;
111
112 if ( tail >= head )
113 {
114 // Is there enough room at the end for the entry?
115 if ( (tail + entrySize) <= dataQueue->queueSize )
116 {
117 entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail);
118
119 entry->size = dataSize;
120 memcpy(&entry->data, data, dataSize);
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
126 dataQueue->tail += entrySize;
127 }
128 else if ( head > entrySize ) // Is there enough room at the beginning?
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;
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
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
182 void 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
202 void 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
221 IOMemoryDescriptor *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