Userspace atomics library
[akaros.git] / user / roslib / src / printf.c
1 // Implementation of cprintf console output for user environments,
2 // based on printfmt() and the sys_cputs() system call.
3 //
4 // cprintf is a debugging statement, not a generic output statement.
5 // It is very important that it always go to the console, especially when
6 // debugging file descriptor code!
7 #ifdef __DEPUTY__
8 #pragma nodeputy
9 #endif
10
11 #include <types.h>
12 #include <stdio.h>
13 #include <stdarg.h>
14 #include <lib.h>
15
16
17 // Collect up to BUF_SIZE characters into a buffer
18 // and perform ONE system call to print all of them,
19 // in order to make the lines output to the console atomic
20 // and prevent interrupts from causing context switches
21 // in the middle of a console output line and such.
22 #define BUF_SIZE 256
23 typedef struct printbuf {
24         int idx;        // current buffer index
25         int cnt;        // total bytes printed so far
26         char buf[BUF_SIZE];
27 } printbuf_t;
28
29
30 static void putch(int ch, printbuf_t **b)
31 {
32         (*b)->buf[(*b)->idx++] = ch;
33         if ((*b)->idx == BUF_SIZE) {
34                 sys_cputs((*b)->buf, (*b)->idx);
35                 (*b)->idx = 0;
36         }
37         (*b)->cnt++;
38 }
39
40 int vcprintf(const char *fmt, va_list ap)
41 {
42         printbuf_t b;
43         printbuf_t *bp = &b;
44
45         b.idx = 0;
46         b.cnt = 0;
47         vprintfmt((void*)putch, (void**)&bp, fmt, ap);
48         sys_cputs(b.buf, b.idx);
49
50         return b.cnt;
51 }
52
53 int cprintf(const char *fmt, ...)
54 {
55         va_list ap;
56         int cnt;
57
58         va_start(ap, fmt);
59         cnt = vcprintf(fmt, ap);
60         va_end(ap);
61
62         return cnt;
63 }
64
65 // Temp async varieties
66 #define MAX_BUFFERS 100
67 POOL_TYPE_DEFINE(printbuf_t, print_buf_pool, MAX_BUFFERS);
68 print_buf_pool_t print_buf_pool;
69
70 static error_t init_printf(void)
71 {
72         POOL_INIT(&print_buf_pool, MAX_BUFFERS);
73         return 0;
74 }
75
76 static printbuf_t* get_free_buffer(void)
77 {
78         return POOL_GET(&print_buf_pool);
79 }
80
81 // This is called when the syscall is waited on
82 static void cputs_async_cleanup(void* data)
83 {
84         POOL_PUT(&print_buf_pool, (printbuf_t*)data);
85 }
86
87 // TODO: its a little difficult to pass back an error through vprintfmt
88 static void putch_async(int ch, printbuf_t **b)
89 {
90         (*b)->buf[(*b)->idx++] = ch;
91         if ((*b)->idx == BUF_SIZE) {
92                 // TODO - should check for a return value for sys_ and get_sys
93                 sys_cputs_async((*b)->buf, (*b)->idx, get_sys_desc(current_async_desc),
94                                 cputs_async_cleanup, *b);
95                 // TODO - should check for a return value
96                 *b = get_free_buffer();
97                 (*b)->idx = 0;
98         }
99         (*b)->cnt++; // supposed to be overall number, not just in one buffer
100 }
101
102 static int vcprintf_async(const char *fmt, va_list ap)
103 {
104         // start with an available buffer.  TODO: check return value
105         printbuf_t* b = get_free_buffer();
106
107         b->idx = 0;
108         b->cnt = 0;
109         vprintfmt((void*)putch_async, (void**)&b, fmt, ap);
110         // TODO - should check for a return value for sys_
111         sys_cputs_async(b->buf, b->idx, get_sys_desc(current_async_desc),
112                         cputs_async_cleanup, b);
113
114         return b->cnt; // this is lying if we used more than one buffer (TODO)
115 }
116
117 int cprintf_async(async_desc_t** desc, const char *fmt, ...)
118 {
119         va_list ap;
120         int cnt;
121
122         // This async call has some housekeeping it needs to do once, ever.
123         static bool initialized = 0;
124         if (!initialized) {
125                 init_printf();
126         initialized = TRUE;
127         }
128         // get a free async_desc for this async call, and save it in the per-thread
129         // tracking variable (current_async_desc).  then pass it back out.
130         // TODO: check return value, return error_t
131         current_async_desc = get_async_desc();
132         *desc = current_async_desc;
133         // This is the traditional (sync) cprintf code
134         va_start(ap, fmt);
135         cnt = vcprintf_async(fmt, ap);
136         va_end(ap);
137
138         // TODO: return cnt via a parameter, and return an error_t?
139         return cnt;
140 }
141