]> git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IODataQueue.cpp
b98060e75a6f523c5a5d62b306e23bd3d4042004
[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 if ( (tail + entrySize) < dataQueue->queueSize )
112 {
113 entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail);
114
115 entry->size = dataSize;
116 memcpy(&entry->data, data, dataSize);
117 dataQueue->tail += entrySize;
118 }
119 else if ( head > entrySize )
120 {
121 // Wrap around to the beginning, but do not allow the tail to catch
122 // up to the head.
123
124 dataQueue->queue->size = dataSize;
125 ((IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail))->size = dataSize;
126 memcpy(&dataQueue->queue->data, data, dataSize);
127 dataQueue->tail = entrySize;
128 }
129 else
130 {
131 return false; // queue is full
132 }
133 }
134 else
135 {
136 // Do not allow the tail to catch up to the head when the queue is full.
137 // That's why the comparison uses a '>' rather than '>='.
138
139 if ( (head - tail) > entrySize )
140 {
141 entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail);
142
143 entry->size = dataSize;
144 memcpy(&entry->data, data, dataSize);
145 dataQueue->tail += entrySize;
146 }
147 else
148 {
149 return false; // queue is full
150 }
151 }
152
153 // Send notification (via mach message) that data is available.
154
155 if ( ( head == tail ) /* queue was empty prior to enqueue() */
156 || ( dataQueue->head == tail ) ) /* queue was emptied during enqueue() */
157 {
158 sendDataAvailableNotification();
159 }
160
161 return true;
162 }
163
164 void IODataQueue::setNotificationPort(mach_port_t port)
165 {
166 static struct _notifyMsg init_msg = { {
167 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0),
168 sizeof (struct _notifyMsg),
169 MACH_PORT_NULL,
170 MACH_PORT_NULL,
171 0,
172 0
173 } };
174
175 if (notifyMsg == 0) {
176 notifyMsg = IOMalloc(sizeof(struct _notifyMsg));
177 }
178
179 *((struct _notifyMsg *)notifyMsg) = init_msg;
180
181 ((struct _notifyMsg *)notifyMsg)->h.msgh_remote_port = port;
182 }
183
184 void IODataQueue::sendDataAvailableNotification()
185 {
186 kern_return_t kr;
187 mach_msg_header_t * msgh;
188
189 msgh = (mach_msg_header_t *)notifyMsg;
190 if (msgh) {
191 kr = mach_msg_send_from_kernel(msgh, msgh->msgh_size);
192 switch(kr) {
193 case MACH_SEND_TIMED_OUT: // Notification already sent
194 case MACH_MSG_SUCCESS:
195 break;
196 default:
197 IOLog("%s: dataAvailableNotification failed - msg_send returned: %d\n", /*getName()*/"IODataQueue", kr);
198 break;
199 }
200 }
201 }
202
203 IOMemoryDescriptor *IODataQueue::getMemoryDescriptor()
204 {
205 IOMemoryDescriptor *descriptor = 0;
206
207 if (dataQueue != 0) {
208 descriptor = IOMemoryDescriptor::withAddress(dataQueue, dataQueue->queueSize + DATA_QUEUE_MEMORY_HEADER_SIZE, kIODirectionOutIn);
209 }
210
211 return descriptor;
212 }
213