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