OpenCores
URL https://opencores.org/ocsvn/s6soc/s6soc/trunk

Subversion Repositories s6soc

[/] [s6soc/] [trunk/] [sw/] [zipos/] [syspipe.c] - Blame information for rev 47

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 22 dgisselq
////////////////////////////////////////////////////////////////////////////////
2
//
3
// Filename:    syspipe.c
4
//
5
// Project:     CMod S6 System on a Chip, ZipCPU demonstration project
6
//
7
// Purpose:     This "device" handles the primary device level interaction of
8
//              almost all devices on the ZipOS: the pipe.  A pipe, as defined
9
//      here, is an O/S supported FIFO.  Information written to the FIFO will
10
//      be read from the FIFO in the order it was received.  Attempts to read
11
//      from an empty FIFO, or equivalently to write to a full FIFO, will block
12
//      the reading (writing) process until memory is available.
13
//
14
// Creator:     Dan Gisselquist, Ph.D.
15
//              Gisselquist Technology, LLC
16
//
17
////////////////////////////////////////////////////////////////////////////////
18
//
19
// Copyright (C) 2015-2016, Gisselquist Technology, LLC
20
//
21
// This program is free software (firmware): you can redistribute it and/or
22
// modify it under the terms of  the GNU General Public License as published
23
// by the Free Software Foundation, either version 3 of the License, or (at
24
// your option) any later version.
25
//
26
// This program is distributed in the hope that it will be useful, but WITHOUT
27
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
28
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
29
// for more details.
30
//
31
// You should have received a copy of the GNU General Public License along
32
// with this program.  (It's in the $(ROOT)/doc directory, run make with no
33
// target there if the PDF file isn't present.)  If not, see
34
// <http://www.gnu.org/licenses/> for a copy.
35
//
36
// License:     GPL, v3, as defined and found on www.gnu.org,
37
//              http://www.gnu.org/licenses/gpl.html
38
//
39
//
40
////////////////////////////////////////////////////////////////////////////////
41
//
42
//
43
#include "errno.h"
44
#include "board.h"
45
#include "taskp.h"
46
#include "syspipe.h"
47
#include "zipsys.h"
48
#include "ktraps.h"
49 45 dgisselq
#include "string.h"
50 22 dgisselq
 
51
#ifndef NULL
52
#define NULL    (void *)0
53
#endif
54
 
55 45 dgisselq
void    kpush_syspipe(SYSPIPE *pipe, char val) {
56 22 dgisselq
        int     tst = (pipe->m_head+1)&pipe->m_mask;
57
        if (tst != pipe->m_tail) {
58
                pipe->m_buf[pipe->m_head] = val;
59
                pipe->m_head = tst;             // Increment the head pointer
60
                if ((pipe->m_rdtask)&&(pipe->m_rdtask != INTERRUPT_READ_TASK))
61
                        pipe->m_rdtask->state = SCHED_READY;
62
        } else pipe->m_error = 1;
63
}
64
 
65 27 dgisselq
extern  void    pipe_panic(SYSPIPE *p);
66 45 dgisselq
/*
67
 * kpop_syspipe
68
 *
69
 * Called from an interrupt context to pop one byte off of the syspipe.
70
 */
71
int     kpop_syspipe(SYSPIPE *pipe, char *vl) {
72 22 dgisselq
        if (pipe->m_head != pipe->m_tail) {
73 45 dgisselq
                // The head will *only* equal the tail if the pipe is empty.
74
                // We come in here, therefore, if the pipe is non-empty
75 22 dgisselq
                *vl = pipe->m_buf[pipe->m_tail];
76 27 dgisselq
                pipe->m_tail = (pipe->m_tail+1)&pipe->m_mask;
77 22 dgisselq
                if (pipe->m_wrtask)
78
                        pipe->m_wrtask->state = SCHED_READY;
79
                return 0;
80 27 dgisselq
        }
81
        return 1; // Error condition
82 22 dgisselq
}
83
 
84 45 dgisselq
/*
85
 * kpop_short_syspipe
86
 *
87
 * This is identical to kpop_syspipe, save that we are pulling a short value
88
 * off of the syspipe (i.e. a pair of chars), not just a single char.
89 27 dgisselq
 */
90 45 dgisselq
int     kpop_short_syspipe(SYSPIPE *pipe, unsigned short *vl) {
91
        if (pipe->m_head == pipe->m_tail)
92
                return 1; // Error condition
93
 
94
        unsigned short *sptr = (unsigned short *)&pipe->m_buf[pipe->m_tail];
95
        // sv = pipe->m_buf[pipe->m_tail];
96
        *vl = *sptr;;
97
        pipe->m_tail = (pipe->m_tail+2)&pipe->m_mask;
98
        if (pipe->m_wrtask)
99
                pipe->m_wrtask->state = SCHED_READY;
100
        return 0;
101
}
102
 
103
/*
104 22 dgisselq
int     len_syspipe(SYSPIPE *p) {
105
        return (p->m_head-p->m_tail) & p->m_mask;
106 27 dgisselq
} */
107 45 dgisselq
 
108 27 dgisselq
/* Returns how many empty spaces are in the pipe
109
 */
110 22 dgisselq
int     num_avail_syspipe(SYSPIPE *p) {
111 27 dgisselq
        // if (head+1 == tail)  PIPE is full
112
        //      (mask+tail-tail+1)=mask+1 &mask = 0
113
        // if (head == tail) PIPE is empty
114
        //      (mask+tail-tail)=mask & mask = mask
115
        // if (head == tail+2) PIPE has two within it
116
        //      (mask+tail-tail-2)=mask-2 & mask = mask-2
117
        //
118
        return (p->m_tail-p->m_head-1) & p->m_mask;
119 22 dgisselq
}
120
 
121
// This will be called from a user context.
122
// Another task may write to the pipe during this call.  If the pipe becomes
123
// full, that task will block.
124
//
125 45 dgisselq
static int uread_syspipe(TASKP tsk __attribute__((__unused__)),
126
                SYSPIPE *p, char *dst, int len) {
127 22 dgisselq
        int nleft= len, h;
128
        if (len == 0) {
129
                // We'll only get here if we were released from within a 
130
                // writing task.
131
                return p->m_nread;
132
        } else do {
133
                // We have a valid read request, for a new process.  Continue
134
                // 'reading' until we have fulfilled the request.
135
                //
136
                // We can read from head, just not write to it
137
                // As for the tail pointer -- we own it, no one else can touch
138
                // it.
139
                h = ((volatile SYSPIPE *)p)->m_head;
140
                if (h < p->m_tail) {
141
                        // The buffer wraps around the end.  Thus, we first
142
                        // read anything between the tail pointer and the end
143
                        int ln1 = p->m_mask+1 - p->m_tail; // Navail to be read
144
                        ln1 = (ln1 > nleft) ? nleft : ln1;
145
                        if (ln1 > 0) {
146 45 dgisselq
                                memcpy(dst, &p->m_buf[p->m_tail], ln1);
147
                                dst += ln1;
148 22 dgisselq
 
149
                                p->m_nread += ln1;
150
                                nleft -= ln1;
151 27 dgisselq
 
152
                                int nt = p->m_tail+ln1;
153
                                if ((unsigned)nt > p->m_mask)
154
                                        nt = 0;
155
                                p->m_tail = nt;
156 22 dgisselq
                        }
157
 
158
                        // nleft is either zero, or tail
159
                        if (nleft & -2)
160
                                exit(nleft);
161
                        else if (p->m_nread & -2)
162
                                exit(p->m_nread);
163
                }
164
 
165
                // Then repeat with the second half of the buffer, from the
166
                // beginning to the head--unless we've exhausted our buffer.
167
                if (nleft > 0) {
168
                        // Still need to do more, wrap around our buffer and
169
                        // restart
170
                        int ln1 = h - p->m_tail;
171
                        ln1 = (ln1 < nleft) ? ln1 : nleft;
172
 
173 45 dgisselq
                        memcpy(dst, &p->m_buf[p->m_tail], ln1);
174
                        dst += ln1;
175 22 dgisselq
 
176
                        p->m_nread += ln1;
177
                        nleft -= ln1;
178 27 dgisselq
                        int nt = p->m_tail+ln1; // nt = new tail value
179
                        if ((unsigned)nt > p->m_mask)
180
                                nt = 0;
181
                        p->m_tail = nt;
182 22 dgisselq
 
183
                        if (nleft & -2)
184
                                exit(nleft);
185
                        else if (p->m_nread & -2)
186
                                exit(p->m_nread);
187
                }
188
 
189
                if (nleft == 0)
190
                        break;
191
 
192
                // We didn't finish our read, check for a blocked writing
193
                // process to copy directly from.  Note that we don't need
194
                // to check the status of the write task--if it is set and
195
                // we are active, then it is blocked and waiting for us to
196
                // complete.  Note also that this is treated as a volatile
197
                // pointer.  It can change from one time through our loop
198
                // to the next.
199
                if (((volatile SYSPIPE *)p)->m_wrtask) {
200 45 dgisselq
                        int     ln;
201
                        char    *src;
202 22 dgisselq
 
203
                        // If the head changed before the write task blocked,
204
                        // then go around again and copy some more before
205
                        // getting started
206
                        //
207
                        // This should never happen, however.  If a write task
208
                        // gets assigned while a read task exists, it doesn't
209
                        // write its values into the buffer, it just waits.
210
                        // therefore we don't need to check for this.
211
                        //
212
                        // if (p->m_head != h)
213
                        //      continue;
214
 
215
                        ln = nleft;
216
                        if (p->m_wrtask->context[4] < nleft)
217
                                ln = p->m_wrtask->context[4];
218 45 dgisselq
                        src = (char *)p->m_wrtask->context[3];
219
                        memcpy(dst, src, ln);
220
                        dst += ln;
221
                        src += ln;
222 22 dgisselq
 
223
                        p->m_nwritten += ln;
224
                        p->m_nread    += ln;
225
 
226
                        nleft -= ln;
227
                        p->m_wrtask->context[4] -= ln;
228
                        p->m_wrtask->context[3]  = (int)src;
229
 
230
                        // We have exhausted the write task.  Release it
231
                        if (p->m_wrtask->context[4] == 0) { // wr_len == 0
232
                                // Release the write task, it has exhausted
233
                                // its buffer
234
                                TASKP   w = p->m_wrtask;
235
                                // Now we allow other tasks to write into our
236
                                // pipe
237
                                p->m_wrtask = 0;
238
                                // And here we actually release the writing
239
                                // task
240
                                w->state = SCHED_READY;
241
                        }
242
                }
243
 
244
                // Realistically, we need to block here 'till more data is
245
                // available.  Need to determine how to do that.  Until then,
246
                // we'll just tell the scheduler to yield.  This will in
247
                // effect create a busy wait--not what we want, but it'll work.
248
                if (nleft > 0) {
249
                        DISABLE_INTS();
250
                        h = ((volatile SYSPIPE *)p)->m_head;
251
                        if (h == p->m_tail)
252
                                wait(0,-1);
253
                        else
254
                                ENABLE_INTS();
255
                }
256
        } while(nleft > 0);
257
 
258
        len = p->m_nread;
259
        p->m_nread = 0;
260
        // Release our ownership of the read end of the pipe
261
        DISABLE_INTS();
262
        p->m_rdtask = NULL;
263
        if (((volatile SYSPIPE *)p)->m_wrtask)
264
                p->m_wrtask->state = SCHED_READY;
265
        ENABLE_INTS();
266
 
267
        // We have accomplished our read
268
        //
269
        return len;
270
}
271
 
272 27 dgisselq
static int      uwrite_syspipe(TASKP tsk __attribute__((__unused__)),
273 45 dgisselq
                SYSPIPE *p, char *src, int len) {
274 22 dgisselq
        int nleft = len;
275
 
276
        // The kernel guarantees, before we come into here, that we have a 
277
        // valid write request.  
278
        do {
279
                // We try to fill this request without going through the pipes
280
                // memory at all.  Hence, if there is a read task that is
281
                // waiting/suspended, waiting on a write (this must've happened
282
                // since we started)--write directly into the read buffer first.
283
 
284
                // If there is a read task blocked, the pipe must be empty
285
                TASKP rdtask = ((volatile SYSPIPE *)p)->m_rdtask;
286
                if (rdtask == INTERRUPT_READ_TASK) {
287
                        // We need to copy everything to the buffer
288
                } else if (rdtask) {
289
                        int ln = nleft;
290
                        if (ln > p->m_rdtask->context[4])
291
                                ln = p->m_rdtask->context[4];
292 45 dgisselq
                        memcpy((char *)p->m_rdtask->context[3], src, ln);
293
                        src += ln;
294 22 dgisselq
                        p->m_nread += ln;
295
                        p->m_rdtask->context[3]+= ln;
296
                        p->m_rdtask->context[4]-= ln;
297
                        nleft -= ln;
298
                        p->m_nwritten += ln;
299
 
300
                        // Realistically, we always need to wake up the reader
301
                        // at this point.  Either 1) we exhausted the readers
302
                        // buffer, or 2) we exhausted our own and the reader
303
                        // needs to take over.  Here, we only handle the first
304
                        // case, leaving the rest for later.
305
                        if (p->m_rdtask->context[4] == 0) {
306
                                TASKP   r = p->m_rdtask;
307
                                // Detach the reader task
308
                                p->m_rdtask = 0;
309
                                // Wake up the reader
310
                                r->state = SCHED_READY;
311
                        }
312
 
313
                        // While it might appear that we might close our loop
314
                        // here, that's not quite the case.  It may be that the
315
                        // pipe is read from an interrupt context.  In that
316
                        // case, there will never be any reader tasks, but we
317
                        // will still need to loop.
318
 
319
                        // Now that we've filled any existing reader task, we
320
                        // check whether or not we fit into the buffer.  The
321
                        // rule is: don't write into the buffer unless
322
                        // everything will fit.  Why?  Well, if you have to
323
                        // block anyway, why not see if you can't avoid a
324
                        // double copy?
325
                        if (nleft == 0)
326
                                break;
327
                }
328
 
329
                // Copy whatever we have into the pipe's buffer
330 27 dgisselq
                int     navail = num_avail_syspipe(p);
331
                if ((nleft <= navail)
332
                        ||((rdtask == INTERRUPT_READ_TASK)&&(navail>0))) {
333 22 dgisselq
                        // Either there is no immediate reader task, or
334
                        // the reader has been exhausted, but we've go
335
                        // more to write.
336
                        //
337
                        // Note that we no longer need to check what
338
                        // will fit into the pipe.  We know the entire
339
                        // rest of our buffer will fit.
340
 
341
                        { // Write into the first half of the pipe
342 27 dgisselq
                        // Be careful not to change head until all is written
343
                        // so that it remains consistent under interrupt
344
                        // conditions.
345 22 dgisselq
                                int ln = p->m_mask+1-p->m_head;
346
                                if (ln > nleft) ln = nleft;
347 27 dgisselq
                                if (ln > navail) ln = navail;
348 22 dgisselq
 
349 45 dgisselq
                                memcpy((void *)&p->m_buf[p->m_head], src, ln);
350
                                src += ln;
351 22 dgisselq
 
352 27 dgisselq
                                p->m_head = (p->m_head+ln)&p->m_mask;
353 45 dgisselq
                                nleft           -= ln;
354
                                p->m_nwritten   += ln;
355
                                navail          -= ln;
356 22 dgisselq
                        }
357
                }
358
 
359 27 dgisselq
                if ((nleft > 0)&&(navail == 0)) {
360 22 dgisselq
                        if (rdtask == INTERRUPT_READ_TASK) {
361
                                DISABLE_INTS();
362 27 dgisselq
                                if (0==num_avail_syspipe(p))
363 22 dgisselq
                                        wait(0,-1);
364
                                else ENABLE_INTS();
365
                        } else {
366
                                DISABLE_INTS();
367
                                if (!((volatile SYSPIPE *)p)->m_rdtask)
368
                                        wait(0,-1); // Should really be a wait
369
                                        // on JIFFIES and if JIFFIES expired
370
                                        // (i.e. write timeout) then break;
371
                                else ENABLE_INTS();
372
                        }
373
                }
374
        } while(nleft > 0);
375
 
376
        int     nw= p->m_nwritten;
377
        p->m_wrtask = 0;
378
        return nw;
379
}
380
 
381
// This will be called from a kernel (interrupt) context
382 45 dgisselq
void    kread_syspipe(TASKP tsk, int dev, char *dst, int len) {
383 22 dgisselq
        SYSPIPE *p = (SYSPIPE *)dev;
384
        if (p->m_rdtask != NULL) {
385
                // If the pipe already has a read task, then we fail
386
                tsk->context[1] = -EBUSY;
387
                zip_halt();
388
        } else if (p->m_error) {
389
                // If there's been an overrun, let the reader know on the
390
                // next read--i.e. this one.  Also, clear the error condition
391
                // so that the following read will succeed.
392
                tsk->context[1] = -EIO;
393
                p->m_tail = p->m_head;
394
                p->m_error = 0;
395
        } else if (len <= 0) {
396
                tsk->context[1] = -EFAULT;
397
                zip_halt();
398
        } else if (!valid_ram_region(dst, len)) {
399
                // Bad parameters
400
                tsk->context[1] = -EFAULT;
401
                zip_halt();
402
        } else {
403
                // Take  ownership of the read end of the pipe
404
                p->m_rdtask = tsk;
405
                p->m_nread  = 0;
406
                tsk->context[1] = (int)tsk;
407
                tsk->context[2] = (int)p;
408
                // These are  already set, else we'd set them again
409
                // tsk->context[3] = (int)dst;
410
                // tsk->context[4] = len;
411
                tsk->context[15] = (int)uread_syspipe;
412
 
413
                // If there is already a write task, make sure it is awake
414
                if (p->m_wrtask) {
415
                        tsk->state = SCHED_WAITING;
416
                        p->m_wrtask->state = SCHED_READY;
417
                } else if (p->m_head == p->m_tail)
418
                        // If the pipe is empty, block the read task
419
                        tsk->state = SCHED_WAITING;
420
 
421
                // On return, this will bring us back to user space, inside our
422
                // user space version of the read system call
423
        }
424
}
425
 
426 45 dgisselq
void    kwrite_syspipe(TASKP tsk, int dev, char *src, int len) {
427 22 dgisselq
        SYSPIPE *p = (SYSPIPE *)dev;
428
        if (p->m_wrtask != NULL) {
429
                // If the pipe already has a write task, then we fail
430
                tsk->context[1] = -EBUSY;
431
        } else if (len <= 0) {
432
                tsk->context[1] = -EFAULT;
433
        } else if (!valid_mem_region(src, len)) {
434
                // Bad parameters
435
                tsk->context[1] = -EFAULT;
436
                zip_halt();
437
        } else {
438
                // Take  ownership of the write end of the pipe
439
                p->m_wrtask    = tsk;
440
                p->m_nwritten = 0;
441
                tsk->context[1] = (int)tsk;
442
                tsk->context[2] = (int)p;
443
                // These are  already set, else we'd set them again
444
                // tsk->context[3] = (int)src;
445
                // tsk->context[4] = len;
446
                tsk->context[15] = (int)uwrite_syspipe;
447
 
448
                // If a reader task currently exists, then block until that
449
                // task either finishes or releases us
450
                if ((p->m_rdtask)&&(p->m_rdtask != INTERRUPT_READ_TASK)) {
451
                        tsk->state = SCHED_WAITING;
452
                        p->m_rdtask->state = SCHED_READY;
453
                } else if (((p->m_head+1)&p->m_mask) == (unsigned)p->m_tail)
454
                        // If the pipe is empty, block until there's data
455
                        tsk->state = SCHED_WAITING;
456
 
457
                // On return, this will bring us back to user space, in our
458
                // user space write call
459
        }
460
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.