/* ------------------------------------------------------------ SFIFO 1.3 ------------------------------------------------------------ * Simple portable lock-free FIFO * (c) 2000-2002, David Olofson * Released under the GNU LESSER GENERAL PUBLIC LICENSE Version 2.1 ----------------------------------------------------------- TODO: * Is there a way to avoid losing one byte of buffer space to avoid extra variables or locking? * Test more compilers and environments. ----------------------------------------------------------- */ #include "config.h" #include "lowlevel.h" #if defined SOUND_SDL || defined SOUND_COREAUDIO #ifdef __KERNEL__ # include # include # include # define malloc(x) kmalloc(x, GFP_KERNEL) # define free(x, y) kfree_s(x, y) #else # include # include # define free(x, y) free(x) #endif #include "sfifo.h" #ifdef _SFIFO_TEST_ #include #include #include #define DBG(x) /*(x)*/ #define TEST_BUFSIZE 10 #else #define DBG(x) #endif /* * Alloc buffer, init FIFO etc... */ int sfifo_init(sfifo_t *f, int size) { memset(f, 0, sizeof(sfifo_t)); if(size > SFIFO_MAX_BUFFER_SIZE) return -EINVAL; /* * Set sufficient power-of-2 size. * * No, there's no bug. If you need * room for N bytes, the buffer must * be at least N+1 bytes. (The fifo * can't tell 'empty' from 'full' * without unsafe index manipulations * otherwise.) */ f->size = 1; for(; f->size <= size; f->size <<= 1) ; /* Get buffer */ if( 0 == (f->buffer = (void *)malloc(f->size)) ) return -ENOMEM; return 0; } /* * Dealloc buffer etc... */ void sfifo_close(sfifo_t *f) { if(f->buffer) free(f->buffer, f->size); } /* * Empty FIFO buffer */ void sfifo_flush(sfifo_t *f) { /* Reset positions */ f->readpos = 0; f->writepos = 0; } /* * Write bytes to a FIFO * Return number of bytes written, or an error code */ int sfifo_write(sfifo_t *f, const void *_buf, int len) { int total; int i; const char *buf = (const char *)_buf; if(!f->buffer) return -ENODEV; /* No buffer! */ /* total = len = min(space, len) */ total = sfifo_space(f); DBG(printf("sfifo_space() = %d\n",total)); if(len > total) len = total; else total = len; i = f->writepos; if(i + len > f->size) { memcpy(f->buffer + i, buf, f->size - i); buf += f->size - i; len -= f->size - i; i = 0; } memcpy(f->buffer + i, buf, len); f->writepos = i + len; return total; } #ifdef __KERNEL__ /* * Write bytes to a FIFO from a user space buffer * Return number of bytes written, or an error code */ int sfifo_write_user(sfifo_t *f, const void *buf, int len) { int total; int i; if(!f->buffer) return -ENODEV; /* No buffer! */ /* total = len = min(space, len) */ total = sfifo_space(f); if(len > total) len = total; else total = len; i = f->writepos; if(i + len > f->size) { if(f->size - i) { if(copy_from_user(f->buffer + i, buf, f->size - i)) return -EFAULT; buf += f->size - i; len -= f->size - i; } i = 0; } if(copy_from_user(f->buffer + i, buf, len)) return -EFAULT; f->writepos = i + len; return total; } #endif /* * Read bytes from a FIFO * Return number of bytes read, or an error code */ int sfifo_read(sfifo_t *f, void *_buf, int len) { int total; int i; char *buf = (char *)_buf; if(!f->buffer) return -ENODEV; /* No buffer! */ /* total = len = min(used, len) */ total = sfifo_used(f); DBG(printf("sfifo_used() = %d\n",total)); if(len > total) len = total; else total = len; i = f->readpos; if(i + len > f->size) { memcpy(buf, f->buffer + i, f->size - i); buf += f->size - i; len -= f->size - i; i = 0; } memcpy(buf, f->buffer + i, len); f->readpos = i + len; return total; } #ifdef __KERNEL__ /* * Read bytes from a FIFO into a user space buffer * Return number of bytes read, or an error code */ int sfifo_read_user(sfifo_t *f, void *buf, int len) { int total; int i; if(!f->buffer) return -ENODEV; /* No buffer! */ /* total = len = min(used, len) */ total = sfifo_used(f); if(len > total) len = total; else total = len; i = f->readpos; if(i + len > f->size) { if(f->size - i) { if(copy_to_user(buf, f->buffer + i, f->size - i)) return -EFAULT; buf += f->size - i; len -= f->size - i; } i = 0; } if(copy_to_user(buf, f->buffer + i, len)) return -EFAULT; f->readpos = i + len; return total; } #endif #ifdef _SFIFO_TEST_ void *sender(void *arg) { char buf[TEST_BUFSIZE*2]; int i,j; int cnt = 0; int res; sfifo_t *sf = (sfifo_t *)arg; while(1) { j = sfifo_space(sf); for(i = 0; i < j; ++i) { ++cnt; buf[i] = cnt; } res = sfifo_write(sf, &buf, j); if(res != j) { printf("Write failed!\n"); sleep(1); } else if(res) printf("Wrote %d\n", res); } } int main() { sfifo_t sf; char last = 0; pthread_t thread; char buf[100] = "---------------------------------------"; printf("sfifo_init(&sf, %d) = %d\n", TEST_BUFSIZE, sfifo_init(&sf, TEST_BUFSIZE) ); #if 0 printf("sfifo_write(&sf,\"0123456789\",7) = %d\n", sfifo_write(&sf,"0123456789",7) ); printf("sfifo_write(&sf,\"abcdefghij\",7) = %d\n", sfifo_write(&sf,"abcdefghij",7) ); printf("sfifo_read(&sf,buf,8) = %d\n", sfifo_read(&sf,buf,8) ); buf[20] = 0; printf("buf =\"%s\"\n",buf); printf("sfifo_write(&sf,\"0123456789\",7) = %d\n", sfifo_write(&sf,"0123456789",7) ); printf("sfifo_read(&sf,buf,10) = %d\n", sfifo_read(&sf,buf,10) ); buf[20] = 0; printf("buf =\"%s\"\n",buf); #else pthread_create(&thread, NULL, sender, &sf); while(1) { static int c = 0; ++last; while( sfifo_read(&sf,buf,1) != 1 ) /*sleep(1)*/; if(last != buf[0]) { printf("Error %d!\n", buf[0]-last); last = buf[0]; } else printf("Ok. (%d)\n",++c); } #endif sfifo_close(&sf); printf("sfifo_close(&sf)\n"); return 0; } #endif #endif /* SOUND_SDL */