| /* Implementation of a ring buffer for reports |
| * |
| * Copyright 2015 CodeWeavers, Aric Stewart |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include "config.h" |
| #include <stdarg.h> |
| #define NONAMELESSUNION |
| #include "hid.h" |
| |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(hid); |
| |
| #define BASE_BUFFER_SIZE 32 |
| #define MIN_BUFFER_SIZE 2 |
| #define MAX_BUFFER_SIZE 512 |
| |
| struct ReportRingBuffer |
| { |
| UINT start, end, size; |
| |
| int *pointers; |
| UINT pointer_alloc; |
| UINT buffer_size; |
| |
| CRITICAL_SECTION lock; |
| |
| BYTE *buffer; |
| }; |
| |
| struct ReportRingBuffer* RingBuffer_Create(UINT buffer_size) |
| { |
| struct ReportRingBuffer *ring; |
| TRACE("Create Ring Buffer with buffer size %i\n",buffer_size); |
| ring = HeapAlloc(GetProcessHeap(), 0, sizeof(*ring)); |
| if (!ring) |
| return NULL; |
| ring->start = ring->end = 0; |
| ring->size = BASE_BUFFER_SIZE; |
| ring->buffer_size = buffer_size; |
| ring->pointer_alloc = 2; |
| ring->pointers = HeapAlloc(GetProcessHeap(), 0, sizeof(int) * ring->pointer_alloc); |
| if (!ring->pointers) |
| { |
| HeapFree(GetProcessHeap(), 0, ring); |
| return NULL; |
| } |
| memset(ring->pointers, 0xff, sizeof(int) * ring->pointer_alloc); |
| ring->buffer = HeapAlloc(GetProcessHeap(), 0, buffer_size * ring->size); |
| if (!ring->buffer) |
| { |
| HeapFree(GetProcessHeap(), 0, ring->pointers); |
| HeapFree(GetProcessHeap(), 0, ring); |
| return NULL; |
| } |
| InitializeCriticalSection(&ring->lock); |
| ring->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": RingBuffer.lock"); |
| return ring; |
| } |
| |
| void RingBuffer_Destroy(struct ReportRingBuffer *ring) |
| { |
| HeapFree(GetProcessHeap(), 0, ring->buffer); |
| HeapFree(GetProcessHeap(), 0, ring->pointers); |
| ring->lock.DebugInfo->Spare[0] = 0; |
| DeleteCriticalSection(&ring->lock); |
| HeapFree(GetProcessHeap(), 0, ring); |
| } |
| |
| UINT RingBuffer_GetBufferSize(struct ReportRingBuffer *ring) |
| { |
| return ring->buffer_size; |
| } |
| |
| UINT RingBuffer_GetSize(struct ReportRingBuffer *ring) |
| { |
| return ring->size; |
| } |
| |
| NTSTATUS RingBuffer_SetSize(struct ReportRingBuffer *ring, UINT size) |
| { |
| BYTE* new_buffer; |
| int i; |
| |
| if (size < MIN_BUFFER_SIZE || size > MAX_BUFFER_SIZE || size == ring->size) |
| return STATUS_INVALID_PARAMETER; |
| |
| EnterCriticalSection(&ring->lock); |
| ring->start = ring->end = 0; |
| for (i = 0; i < ring->pointer_alloc; i++) |
| { |
| if (ring->pointers[i] != 0xffffffff) |
| ring->pointers[i] = 0; |
| } |
| new_buffer = HeapAlloc(GetProcessHeap(), 0, ring->buffer_size * size); |
| if (!new_buffer) |
| { |
| LeaveCriticalSection(&ring->lock); |
| return STATUS_NO_MEMORY; |
| } |
| HeapFree(GetProcessHeap(), 0, ring->buffer); |
| ring->buffer = new_buffer; |
| ring->size = size; |
| LeaveCriticalSection(&ring->lock); |
| return STATUS_SUCCESS; |
| } |
| |
| void RingBuffer_Read(struct ReportRingBuffer *ring, UINT index, void *output, UINT *size) |
| { |
| void *ret = NULL; |
| |
| EnterCriticalSection(&ring->lock); |
| if (index >= ring->pointer_alloc || ring->pointers[index] == 0xffffffff) |
| { |
| LeaveCriticalSection(&ring->lock); |
| *size = 0; |
| return; |
| } |
| if (ring->pointers[index] == ring->end) |
| { |
| LeaveCriticalSection(&ring->lock); |
| *size = 0; |
| } |
| else |
| { |
| ret = &ring->buffer[ring->pointers[index] * ring->buffer_size]; |
| memcpy(output, ret, ring->buffer_size); |
| ring->pointers[index]++; |
| if (ring->pointers[index] == ring->size) |
| ring->pointers[index] = 0; |
| LeaveCriticalSection(&ring->lock); |
| *size = ring->buffer_size; |
| } |
| } |
| |
| UINT RingBuffer_AddPointer(struct ReportRingBuffer *ring) |
| { |
| UINT idx; |
| EnterCriticalSection(&ring->lock); |
| for (idx = 0; idx < ring->pointer_alloc; idx++) |
| if (ring->pointers[idx] == -1) |
| break; |
| if (idx >= ring->pointer_alloc) |
| { |
| int count = idx = ring->pointer_alloc; |
| ring->pointer_alloc *= 2; |
| ring->pointers = HeapReAlloc(GetProcessHeap(), 0, ring->pointers, sizeof(int) * ring->pointer_alloc); |
| for( ;count < ring->pointer_alloc; count++) |
| ring->pointers[count] = -1; |
| } |
| ring->pointers[idx] = ring->start; |
| LeaveCriticalSection(&ring->lock); |
| return idx; |
| } |
| |
| void RingBuffer_RemovePointer(struct ReportRingBuffer *ring, UINT index) |
| { |
| EnterCriticalSection(&ring->lock); |
| if (index < ring->pointer_alloc) |
| ring->pointers[index] = 0xffffffff; |
| LeaveCriticalSection(&ring->lock); |
| } |
| |
| void RingBuffer_Write(struct ReportRingBuffer *ring, void *data) |
| { |
| UINT i; |
| |
| EnterCriticalSection(&ring->lock); |
| memcpy(&ring->buffer[ring->end * ring->buffer_size], data, ring->buffer_size); |
| ring->end++; |
| if (ring->end == ring->size) |
| ring->end = 0; |
| if (ring->start == ring->end) |
| { |
| ring->start++; |
| if (ring->start == ring->size) |
| ring->start = 0; |
| } |
| for (i = 0; i < ring->pointer_alloc; i++) |
| if (ring->pointers[i] == ring->end) |
| ring->pointers[i] = ring->start; |
| LeaveCriticalSection(&ring->lock); |
| } |