1 /**\r
2 * Copyright (C) ARM Limited 2010-2012. All rights reserved.\r
3 *\r
4 * This program is free software; you can redistribute it and/or modify\r
5 * it under the terms of the GNU General Public License version 2 as\r
6 * published by the Free Software Foundation.\r
7 */\r
8 \r
9 #include <stdlib.h>\r
10 #include <string.h>\r
11 #include <unistd.h>\r
12 #include "Fifo.h"\r
13 #include "Logging.h"\r
14 \r
15 extern void handleException();\r
16 \r
17 // bufferSize is the amount of data to be filled\r
18 // singleBufferSize is the maximum size that may be filled during a single write\r
19 // (bufferSize + singleBufferSize) will be allocated\r
20 Fifo::Fifo(int singleBufferSize, int bufferSize) {\r
21 mWrite = mRead = mReadCommit = mRaggedEnd = 0;\r
22 mWrapThreshold = bufferSize;\r
23 mSingleBufferSize = singleBufferSize;\r
24 mBuffer = (char*)valloc(bufferSize + singleBufferSize);\r
25 mEnd = false;\r
26 \r
27 if (mBuffer == NULL) {\r
28 logg->logError(__FILE__, __LINE__, "failed to allocate %d bytes", bufferSize + singleBufferSize);\r
29 handleException();\r
30 }\r
31 \r
32 if (sem_init(&mWaitForSpaceSem, 0, 0) || sem_init(&mWaitForDataSem, 0, 0)) {\r
33 logg->logError(__FILE__, __LINE__, "sem_init() failed");\r
34 handleException();\r
35 }\r
36 }\r
37 \r
38 Fifo::~Fifo() {\r
39 free(mBuffer);\r
40 }\r
41 \r
42 int Fifo::numBytesFilled() {\r
43 return mWrite - mRead + mRaggedEnd;\r
44 }\r
45 \r
46 char* Fifo::start() {\r
47 return mBuffer;\r
48 }\r
49 \r
50 bool Fifo::isEmpty() {\r
51 return mRead == mWrite;\r
52 }\r
53 \r
54 bool Fifo::isFull() {\r
55 return willFill(0);\r
56 }\r
57 \r
58 // Determines if the buffer will fill assuming 'additional' bytes will be added to the buffer\r
59 // comparisons use '<', read and write pointers must never equal when not empty\r
60 // 'full' means there is less than singleBufferSize bytes available; it does not mean there are zero bytes available\r
61 bool Fifo::willFill(int additional) {\r
62 if (mWrite > mRead) {\r
63 if (numBytesFilled() + additional < mWrapThreshold) {\r
64 return false;\r
65 }\r
66 } else {\r
67 if (numBytesFilled() + additional < mWrapThreshold - mSingleBufferSize) {\r
68 return false;\r
69 }\r
70 }\r
71 return true;\r
72 }\r
73 \r
74 // This function will stall until contiguous singleBufferSize bytes are available\r
75 char* Fifo::write(int length) {\r
76 if (length <= 0) {\r
77 length = 0;\r
78 mEnd = true;\r
79 }\r
80 \r
81 // update the write pointer\r
82 mWrite += length;\r
83 \r
84 // handle the wrap-around\r
85 if (mWrite >= mWrapThreshold) {\r
86 mRaggedEnd = mWrite;\r
87 mWrite = 0;\r
88 }\r
89 \r
90 // send a notification that data is ready\r
91 sem_post(&mWaitForDataSem);\r
92 \r
93 // wait for space\r
94 while (isFull()) {\r
95 sem_wait(&mWaitForSpaceSem);\r
96 }\r
97 \r
98 return &mBuffer[mWrite];\r
99 }\r
100 \r
101 // This function will stall until data is available\r
102 char* Fifo::read(int* length) {\r
103 // update the read pointer now that the data has been handled\r
104 mRead = mReadCommit;\r
105 \r
106 // handle the wrap-around\r
107 if (mRead >= mWrapThreshold) {\r
108 mRaggedEnd = mRead = mReadCommit = 0;\r
109 }\r
110 \r
111 // send a notification that data is free (space is available)\r
112 sem_post(&mWaitForSpaceSem);\r
113 \r
114 // wait for data\r
115 while (isEmpty() && !mEnd) {\r
116 sem_wait(&mWaitForDataSem);\r
117 }\r
118 \r
119 // obtain the length\r
120 do {\r
121 mReadCommit = mRaggedEnd ? mRaggedEnd : mWrite;\r
122 *length = mReadCommit - mRead;\r
123 } while (*length < 0); // plugs race condition without using semaphores\r
124 \r
125 return &mBuffer[mRead];\r
126 }\r