1/* 2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29#define DISABLE_DATAQUEUE_WARNING 30 31#include <IOKit/IODataQueue.h> 32 33#undef DISABLE_DATAQUEUE_WARNING 34 35#include <IOKit/IODataQueueShared.h> 36#include <IOKit/IOLib.h> 37#include <IOKit/IOMemoryDescriptor.h> 38#include <libkern/OSAtomic.h> 39 40#ifdef enqueue 41#undef enqueue 42#endif 43 44#ifdef dequeue 45#undef dequeue 46#endif 47 48#define super OSObject 49 50OSDefineMetaClassAndStructors(IODataQueue, OSObject) 51 52IODataQueue *IODataQueue::withCapacity(UInt32 size) 53{ 54 IODataQueue *dataQueue = new IODataQueue; 55 56 if (dataQueue) { 57 if (!dataQueue->initWithCapacity(size)) { 58 dataQueue->release(); 59 dataQueue = 0; 60 } 61 } 62 63 return dataQueue; 64} 65 66IODataQueue *IODataQueue::withEntries(UInt32 numEntries, UInt32 entrySize) 67{ 68 IODataQueue *dataQueue = new IODataQueue; 69 70 if (dataQueue) { 71 if (!dataQueue->initWithEntries(numEntries, entrySize)) { 72 dataQueue->release(); 73 dataQueue = 0; 74 } 75 } 76 77 return dataQueue; 78} 79 80Boolean IODataQueue::initWithCapacity(UInt32 size) 81{ 82 vm_size_t allocSize = 0; 83 84 if (!super::init()) { 85 return false; 86 } 87 88 if (size > UINT32_MAX - DATA_QUEUE_MEMORY_HEADER_SIZE) { 89 return false; 90 } 91 92 allocSize = round_page(size + DATA_QUEUE_MEMORY_HEADER_SIZE); 93 94 if (allocSize < size) { 95 return false; 96 } 97 98 dataQueue = (IODataQueueMemory *)IOMallocAligned(allocSize, PAGE_SIZE); 99 if (dataQueue == 0) { 100 return false; 101 } 102 103 dataQueue->queueSize = size; 104 dataQueue->head = 0; 105 dataQueue->tail = 0; 106 107 if (!notifyMsg) { 108 notifyMsg = IOMalloc(sizeof(mach_msg_header_t)); 109 if (!notifyMsg) 110 return false; 111 } 112 bzero(notifyMsg, sizeof(mach_msg_header_t)); 113 114 return true; 115} 116 117Boolean IODataQueue::initWithEntries(UInt32 numEntries, UInt32 entrySize) 118{ 119 // Checking overflow for (numEntries + 1)*(entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE): 120 // check (entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE) 121 if ((entrySize > UINT32_MAX - DATA_QUEUE_ENTRY_HEADER_SIZE) || 122 // check (numEntries + 1) 123 (numEntries > UINT32_MAX-1) || 124 // check (numEntries + 1)*(entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE) 125 (entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE > UINT32_MAX/(numEntries+1))) { 126 return false; 127 } 128 129 return (initWithCapacity((numEntries + 1) * (DATA_QUEUE_ENTRY_HEADER_SIZE + entrySize))); 130} 131 132void IODataQueue::free() 133{ 134 if (dataQueue) { 135 IOFreeAligned(dataQueue, round_page(dataQueue->queueSize + DATA_QUEUE_MEMORY_HEADER_SIZE)); 136 dataQueue = NULL; 137 138 if (notifyMsg) { 139 IOFree(notifyMsg, sizeof(mach_msg_header_t)); 140 notifyMsg = NULL; 141 } 142 } 143 144 super::free(); 145 146 return; 147} 148 149Boolean IODataQueue::enqueue(void * data, UInt32 dataSize) 150{ 151 const UInt32 head = dataQueue->head; // volatile 152 const UInt32 tail = dataQueue->tail; 153 const UInt32 entrySize = dataSize + DATA_QUEUE_ENTRY_HEADER_SIZE; 154 IODataQueueEntry * entry; 155 156 // Check for overflow of entrySize 157 if (dataSize > UINT32_MAX - DATA_QUEUE_ENTRY_HEADER_SIZE) { 158 return false; 159 } 160 // Check for underflow of (dataQueue->queueSize - tail) 161 if (dataQueue->queueSize < tail) { 162 return false; 163 } 164 165 if ( tail >= head ) 166 { 167 // Is there enough room at the end for the entry? 168 if ((entrySize <= UINT32_MAX - tail) && 169 ((tail + entrySize) <= dataQueue->queueSize) ) 170 { 171 entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail); 172 173 entry->size = dataSize; 174 memcpy(&entry->data, data, dataSize); 175 176 // The tail can be out of bound when the size of the new entry 177 // exactly matches the available space at the end of the queue. 178 // The tail can range from 0 to dataQueue->queueSize inclusive. 179 180 OSAddAtomic(entrySize, (SInt32 *)&dataQueue->tail); 181 } 182 else if ( head > entrySize ) // Is there enough room at the beginning? 183 { 184 // Wrap around to the beginning, but do not allow the tail to catch 185 // up to the head. 186 187 dataQueue->queue->size = dataSize; 188 189 // We need to make sure that there is enough room to set the size before 190 // doing this. The user client checks for this and will look for the size 191 // at the beginning if there isn't room for it at the end. 192 193 if ( ( dataQueue->queueSize - tail ) >= DATA_QUEUE_ENTRY_HEADER_SIZE ) 194 { 195 ((IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail))->size = dataSize; 196 } 197 198 memcpy(&dataQueue->queue->data, data, dataSize); 199 OSCompareAndSwap(dataQueue->tail, entrySize, &dataQueue->tail); 200 } 201 else 202 { 203 return false; // queue is full 204 } 205 } 206 else 207 { 208 // Do not allow the tail to catch up to the head when the queue is full. 209 // That's why the comparison uses a '>' rather than '>='. 210 211 if ( (head - tail) > entrySize ) 212 { 213 entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail); 214 215 entry->size = dataSize; 216 memcpy(&entry->data, data, dataSize); 217 OSAddAtomic(entrySize, (SInt32 *)&dataQueue->tail); 218 } 219 else 220 { 221 return false; // queue is full 222 } 223 } 224 225 // Send notification (via mach message) that data is available. 226 227 if ( ( head == tail ) /* queue was empty prior to enqueue() */ 228 || ( dataQueue->head == tail ) ) /* queue was emptied during enqueue() */ 229 { 230 sendDataAvailableNotification(); 231 } 232 233 return true; 234} 235 236void IODataQueue::setNotificationPort(mach_port_t port) 237{ 238 mach_msg_header_t * msgh = (mach_msg_header_t *) notifyMsg; 239 240 if (msgh) { 241 bzero(msgh, sizeof(mach_msg_header_t)); 242 msgh->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); 243 msgh->msgh_size = sizeof(mach_msg_header_t); 244 msgh->msgh_remote_port = port; 245 } 246} 247 248void IODataQueue::sendDataAvailableNotification() 249{ 250 kern_return_t kr; 251 mach_msg_header_t * msgh; 252 253 msgh = (mach_msg_header_t *) notifyMsg; 254 if (msgh && msgh->msgh_remote_port) { 255 kr = mach_msg_send_from_kernel_with_options(msgh, msgh->msgh_size, MACH_SEND_TIMEOUT, MACH_MSG_TIMEOUT_NONE); 256 switch(kr) { 257 case MACH_SEND_TIMED_OUT: // Notification already sent 258 case MACH_MSG_SUCCESS: 259 case MACH_SEND_NO_BUFFER: 260 break; 261 default: 262 IOLog("%s: dataAvailableNotification failed - msg_send returned: %d\n", /*getName()*/"IODataQueue", kr); 263 break; 264 } 265 } 266} 267 268IOMemoryDescriptor *IODataQueue::getMemoryDescriptor() 269{ 270 IOMemoryDescriptor *descriptor = 0; 271 272 if (dataQueue != 0) { 273 descriptor = IOMemoryDescriptor::withAddress(dataQueue, dataQueue->queueSize + DATA_QUEUE_MEMORY_HEADER_SIZE, kIODirectionOutIn); 274 } 275 276 return descriptor; 277} 278 279 280