source: svn/trunk/newcon3bcm2_21bu/magnum/basemodules/kni/ucos_ii/bkni_print.c

Last change on this file was 2, checked in by phkim, 11 years ago

1.phkim

  1. revision copy newcon3sk r27
  • Property svn:executable set to *
File size: 9.6 KB
Line 
1/***************************************************************************
2 *         Copyright (c) 2006-2010, Broadcom Corporation
3 *         All Rights Reserved
4 *         Confidential Property of Broadcom Corporation
5 *
6 *      THIS SOFTWARE MAY ONLY BE USED SUBJECT TO AN EXECUTED SOFTWARE LICENSE
7 *      AGREEMENT  BETWEEN THE USER AND BROADCOM.  YOU HAVE NO RIGHT TO USE OR
8 *      EXPLOIT THIS MATERIAL EXCEPT SUBJECT TO THE TERMS OF SUCH AN AGREEMENT.
9 *
10 * $brcm_Workfile: bkni_print.c $
11 * $brcm_Revision: Hydra_Software_Devel/1 $
12 * $brcm_Date: 8/19/10 7:55a $
13 *
14 * Module Description: Background printf module
15 *
16 ***************************************************************************/
17
18#include "bstd.h"
19#include "bdbg.h"
20#include "bkni.h"
21#include "bkni_multi.h"
22#include "ministd.h"
23#include "bkni_print.h"
24
25/**
26   Non blocking print works as follows:
27   1. Block is atomically allocated from circular buffer.
28   2. vsnprinf from standard lirary is used to print message in to this block.
29   3. If (2) was not preempted by another print call block is atomically
30      reduced to the size actually occupied by message. Freed memory released
31      back to the circular buffer.
32   4. If (2) was preempted by higher priority print, block size is left at the
33      original size. This results in some amount of wasted memory but allows
34      concurrent printing from interrupts and tasks.
35   Allocation and release are around 80 mips instructions each. Interrupts are
36   never locked during allocation and release of memory. If atomic operation
37   is interrupted it will return error and procedure will be retried.
38   Atomic compare and swap function is used to implement atomic allocation
39   and release operations. This function is coded using LL and SC mips
40   instructions.
41 */
42
43extern unsigned CAS(void * addr, unsigned compare, unsigned swap);
44
45#define RING_SIZE 0x80000
46#define MIN_FREE_SPACE 0x200
47
48static char print_ring_buffer[RING_SIZE];
49
50struct BKNI_Print_Ring_t {
51    char * start;
52    char * end;
53    char * write;
54    char * read;
55    char * wrap;
56    int overflow;
57    BKNI_EventHandle evt;
58    unsigned writer;
59};
60
61#define BP_SIZE_T unsigned
62#define MAX_DATA_SIZE (MIN_FREE_SPACE - sizeof(BP_SIZE_T))
63struct BKNI_Print_Block_t {
64    BP_SIZE_T size;
65    char data[MAX_DATA_SIZE];
66};
67
68static struct BKNI_Print_Ring_t ring;
69
70void BKNI_Print_Init(void)
71{
72    BERR_Code berr;
73    BSTD_UNUSED(berr);
74
75    ring.start = print_ring_buffer;
76    ring.end = print_ring_buffer + RING_SIZE - MIN_FREE_SPACE;
77    ring.write = print_ring_buffer;
78    ring.read = print_ring_buffer;
79    ring.wrap = ring.end + MIN_FREE_SPACE;
80    ring.writer = 0;
81   
82    berr = BKNI_CreateEvent(&ring.evt);
83    BDBG_ASSERT(BERR_SUCCESS == berr);
84    BKNI_ResetEvent(ring.evt);
85}
86
87/**
88   Atomically increment writer counter
89 */
90static inline void BKNI_Print_IncWriter(void)
91{
92    while(1 != CAS(&ring.writer, ring.writer, (ring.writer + 1)));
93}
94
95/**
96   Atomically decrement writer counter
97 */
98static inline void BKNI_Print_DecWriter(void)
99{
100    while(1 != CAS(&ring.writer, ring.writer, (ring.writer - 1)));
101}
102
103/**
104   retrieve read pointer pointing at the data in the ring buffer, if there is
105   no data NULL is returned.
106 */
107static void BKNI_Print_RetrieveRead(char **read)
108{
109    if(0 == ring.writer){
110        if(ring.read < ring.write){
111            *read = ring.read;
112        }else if (ring.read > ring.write){
113            if(ring.read == ring.wrap){
114                ring.wrap = ring.end + MIN_FREE_SPACE;
115                ring.read = ring.start;
116                if(ring.read == ring.write){
117                    *read = NULL;
118                }else{
119                    *read = ring.read;
120                }
121            }else{
122                *read = ring.read;
123            }
124        }else{/*ring.read == ring.write*/
125            *read = NULL;
126        }
127    }else{
128        *read = NULL;
129    }
130}
131
132/**
133   advance read pointer indicating that data is consumed and the ring buffer
134   can be reused for new data.
135*/
136
137static void BKNI_Print_AdvanceRead(unsigned size)
138{
139    char * tmp;
140    tmp = ring.read + size;
141    BDBG_ASSERT(tmp <= ring.wrap);
142    if(ring.read < ring.write){
143        if(tmp <= ring.write){
144            ring.read = tmp;
145        }else{
146            /*circular buffer underflow on read.*/
147            BDBG_ASSERT(0);
148        }
149    }else{
150        if(tmp <= ring.wrap){
151            ring.read = tmp;
152        }else{
153            /*attempt to read past end of buffer*/
154            BDBG_ASSERT(0);
155        }
156    }
157}
158
159/**
160   Atomically allocate block from the ring buffer. Function will return pointer
161   to the block that is MIN_FREE_SPACE size or NULL if no free space
162   is available.
163*/
164static void BKNI_Print_GetBlock(struct BKNI_Print_Block_t ** block)
165{
166    char * old_write;
167    char * new_write;
168
169    do {
170        old_write = ring.write;
171        if(old_write >= ring.read){ /*write ahead of read or buf is empty*/
172            if(old_write < ring.end){
173                new_write = old_write + MIN_FREE_SPACE;
174            }else{
175                if((ring.read - ring.start) > MIN_FREE_SPACE){
176                    new_write = ring.start + MIN_FREE_SPACE;
177                }else{
178                    new_write = NULL; /* no free space */
179                }
180            }
181        }else{
182            if((old_write + MIN_FREE_SPACE) < ring.read){
183                new_write = old_write + MIN_FREE_SPACE;
184            }else{
185                new_write = NULL;   /* no free space */
186            }
187        }
188        /* if there is room allocate block if not return NULL */
189        if(NULL != new_write){
190            if(1 == CAS(&ring.write, (unsigned)old_write, (unsigned)new_write)){
191                if(new_write > old_write){ 
192                    *block = (void*)old_write;
193                    if(new_write >= ring.end){
194                        ring.wrap = new_write;
195                    }
196                }else{/* wrap */
197                    *block = (void*)ring.start;
198                }
199                (*block)->size = MIN_FREE_SPACE;
200                break;
201            }else{
202                continue;
203            }
204        }else{
205            *block = NULL;
206            break;
207        }
208    }while(1);
209}
210
211/**
212   Atomically shrink block to size if possible. If not possible block will
213   be left at original size.
214*/
215static void BKNI_Print_ShrinkBlock(struct BKNI_Print_Block_t * block, BP_SIZE_T size)
216{
217    BP_SIZE_T new_size;
218    char * old_write;
219    char * new_write;
220    /* Align on the BP_SIZE_T plus leave room for header*/
221    new_size = (size + (2 * sizeof(BP_SIZE_T))) & ~(sizeof(BP_SIZE_T) - 1);
222    old_write = (char*)block + MIN_FREE_SPACE;;
223    new_write = (char*)block + new_size;
224    while(ring.write == old_write){
225        if(1 == CAS(&ring.write, (unsigned)old_write, (unsigned)new_write)){
226            block->size = new_size;
227            BDBG_ASSERT(MIN_FREE_SPACE >= block->size);
228            if(old_write == ring.wrap){
229                if(new_write >= ring.end){
230                    ring.wrap = new_write;
231                }else{
232                    ring.wrap = ring.end + MIN_FREE_SPACE;
233                }
234            }
235        }else{
236            continue;
237        }
238    }
239}
240
241int BKNI_Print_Write(const char *ptr, int len)
242{
243    int cnt = 0;
244    struct BKNI_Print_Block_t * block;
245
246    BKNI_Print_IncWriter();
247    BKNI_Print_GetBlock(&block);
248    if (NULL != block)
249        {
250                if (block->size >= len)
251                        cnt = len;
252                else
253                        cnt = block->size;
254
255        BKNI_Memcpy(block->data,ptr,cnt);
256        if(cnt > (MIN_FREE_SPACE - sizeof(BP_SIZE_T))){
257            cnt = MIN_FREE_SPACE - sizeof(BP_SIZE_T);
258        }
259
260        BKNI_Print_ShrinkBlock(block, (cnt + 1));
261        BKNI_Print_DecWriter();
262        BKNI_SetEvent(ring.evt);
263    }
264        else
265        BKNI_Print_DecWriter();
266
267    return cnt;
268}
269
270int BKNI_Print_Vprintf(const char *fmt, va_list ap)
271{
272    int cnt = 0;
273    struct BKNI_Print_Block_t * block;
274
275    BKNI_Print_IncWriter();
276    BKNI_Print_GetBlock(&block);
277    if(NULL != block){
278        cnt = vsnprintf(block->data, MIN_FREE_SPACE - sizeof(BP_SIZE_T), fmt, ap);
279        if(cnt > (MIN_FREE_SPACE - sizeof(BP_SIZE_T))){
280            cnt = MIN_FREE_SPACE - sizeof(BP_SIZE_T);
281        }
282        BKNI_Print_ShrinkBlock(block, (cnt + 1));
283        BKNI_Print_DecWriter();
284        BKNI_SetEvent(ring.evt);
285    }else
286        BKNI_Print_DecWriter();
287
288    return cnt;
289}
290
291int BKNI_Print_Printf(const char *fmt, ...)
292{
293    va_list ap;
294    int cnt;
295   
296    va_start(ap, fmt);
297    cnt = BKNI_Print_Vprintf(fmt, ap);
298    va_end(ap);
299    return cnt;
300}
301
302/* Call in a while loop */
303void BKNI_Print_Worker(BKNI_Print_Out Output)
304{
305    char *end;
306    int cnt;
307    struct BKNI_Print_Block_t * block;
308
309        BKNI_EnterCriticalSection();
310        BKNI_Print_RetrieveRead((void*)&block);
311        BKNI_LeaveCriticalSection();
312        if(NULL != block){
313                BDBG_ASSERT(MIN_FREE_SPACE >= block->size);
314                cnt = 0;
315                end = block->data;
316                while((0 != *end++)&&(cnt++ < MIN_FREE_SPACE)) ;
317                Output(block->data, cnt);
318                BKNI_EnterCriticalSection();
319                BKNI_Print_AdvanceRead(block->size);
320                BKNI_LeaveCriticalSection();
321        }else{
322                BKNI_WaitForEvent(ring.evt, BKNI_INFINITE);
323        }
324}
325
326
327/* Call in a while loop */
328void BKNI_Print_Flush(BKNI_Print_Out Output)
329{
330    char *end;
331    int cnt;
332    struct BKNI_Print_Block_t * block;
333
334        while (1)
335        {
336                BKNI_EnterCriticalSection();
337                BKNI_Print_RetrieveRead((void*)&block);
338                BKNI_LeaveCriticalSection();
339                if(NULL != block){
340                        BDBG_ASSERT(MIN_FREE_SPACE >= block->size);
341                        cnt = 0;
342                        end = block->data;
343                        while((0 != *end++)&&(cnt++ < MIN_FREE_SPACE)) ;
344                        Output(block->data, cnt);
345                        BKNI_EnterCriticalSection();
346                        BKNI_Print_AdvanceRead(block->size);
347                        BKNI_LeaveCriticalSection();
348                }else{
349                        break;
350                }
351        }
352}
353
354/* Please do not remove! */
355/* Local Variables: */
356/* mode: C */
357/* indent-tabs-mode: nil */
358/* End: */
Note: See TracBrowser for help on using the repository browser.