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