first draft for building nozomi
[nozomi.git] / kfifo.c
1 /*
2  * A simple kernel FIFO implementation.
3  *
4  * Copyright (C) 2004 Stelian Pop <stelian@popies.net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  */
21
22 #include <linux/kernel.h>
23 #include <linux/module.h>
24 #include <linux/slab.h>
25 #include <asm/uaccess.h>
26 #include "kfifo.h"
27
28
29 /**
30  * kfifo_init - allocates a new FIFO using a preallocated buffer
31  * @buffer: the preallocated buffer to be used.
32  * @size: the size of the internal buffer, this have to be a power of 2.
33  * @gfp_mask: get_free_pages mask, passed to kmalloc()
34  * @lock: the lock to be used to protect the fifo buffer
35  *
36  * Do NOT pass the kfifo to kfifo_free() after use ! Simply free the
37  * struct kfifo with kfree().
38  */
39 struct kfifo *kfifo_init(unsigned char *buffer, unsigned int size,
40                          unsigned int  gfp_mask, void *lock)
41 {
42         struct kfifo *fifo;
43
44         /* size must be a power of 2 */
45         BUG_ON(size & (size - 1));
46
47         fifo = kmalloc(sizeof(struct kfifo), gfp_mask);
48         if (!fifo)
49                 return ERR_PTR(-ENOMEM);
50
51         fifo->buffer = buffer;
52         fifo->size = size;
53         fifo->in = fifo->out = 0;
54
55         return fifo;
56 }
57
58 /**
59  * kfifo_alloc - allocates a new FIFO and its internal buffer
60  * @size: the size of the internal buffer to be allocated.
61  * @gfp_mask: get_free_pages mask, passed to kmalloc()
62  * @lock: the lock to be used to protect the fifo buffer
63  *
64  * The size will be rounded-up to a power of 2.
65  */
66 struct kfifo *kfifo_alloc(unsigned int size, unsigned int  gfp_mask, void *lock)
67 {
68         unsigned char *buffer;
69         struct kfifo *ret;
70
71         /*
72          * round up to the next power of 2, since our 'let the indices
73          * wrap' tachnique works only in this case.
74          */
75         if (size & (size - 1)) {
76                 BUG_ON(size > 0x80000000);
77                 printk("Do not support no power of two!\n");
78                 //size = roundup_pow_of_two(size);
79         }
80
81         buffer = kmalloc(size, gfp_mask);
82         if (!buffer)
83                 return ERR_PTR(-ENOMEM);
84
85         ret = kfifo_init(buffer, size, gfp_mask, lock);
86
87         if (IS_ERR(ret))
88                 kfree(buffer);
89
90         return ret;
91 }
92
93 /**
94  * kfifo_free - frees the FIFO
95  * @fifo: the fifo to be freed.
96  */
97 void kfifo_free(struct kfifo *fifo)
98 {
99         kfree(fifo->buffer);
100         kfree(fifo);
101 }
102
103 /**
104  * __kfifo_put - puts some data into the FIFO, no locking version
105  * @fifo: the fifo to be used.
106  * @buffer: the data to be added.
107  * @len: the length of the data to be added.
108  *
109  * This function copies at most 'len' bytes from the 'buffer' into
110  * the FIFO depending on the free space, and returns the number of
111  * bytes copied.
112  *
113  * Note that with only one concurrent reader and one concurrent
114  * writer, you don't need extra locking to use these functions.
115  */
116 unsigned int __kfifo_put(struct kfifo *fifo,
117                          unsigned char *buffer, unsigned int len)
118 {
119         unsigned int l;
120
121         len = min(len, fifo->size - fifo->in + fifo->out);
122
123         /* first put the data starting from fifo->in to buffer end */
124         l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));
125         memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);
126
127         /* then put the rest (if any) at the beginning of the buffer */
128         memcpy(fifo->buffer, buffer + l, len - l);
129
130         fifo->in += len;
131
132         return len;
133 }
134
135 /** __kfifio_put_user works like __kfifo_put, but copies data from 
136  *  user space.
137  */
138
139 unsigned int __kfifo_put_user(struct kfifo *fifo,
140                          unsigned char *buffer, unsigned int len)
141 {
142         unsigned int l;
143
144         len = min(len, fifo->size - fifo->in + fifo->out);
145
146         /* first put the data starting from fifo->in to buffer end */
147         l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));
148         copy_from_user(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);
149
150         /* then put the rest (if any) at the beginning of the buffer */
151         copy_from_user(fifo->buffer, buffer + l, len - l);
152
153         fifo->in += len;
154
155         return len;
156 }
157
158
159
160 /**
161  * __kfifo_get - gets some data from the FIFO, no locking version
162  * @fifo: the fifo to be used.
163  * @buffer: where the data must be copied.
164  * @len: the size of the destination buffer.
165  *
166  * This function copies at most 'len' bytes from the FIFO into the
167  * 'buffer' and returns the number of copied bytes.
168  *
169  * Note that with only one concurrent reader and one concurrent
170  * writer, you don't need extra locking to use these functions.
171  */
172 unsigned int __kfifo_get(struct kfifo *fifo,
173                          unsigned char *buffer, unsigned int len)
174 {
175         unsigned int l;
176
177         len = min(len, fifo->in - fifo->out);
178
179         /* first get the data from fifo->out until the end of the buffer */
180         l = min(len, fifo->size - (fifo->out & (fifo->size - 1)));
181         memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l);
182
183         /* then get the rest (if any) from the beginning of the buffer */
184         memcpy(buffer + l, fifo->buffer, len - l);
185
186         fifo->out += len;
187
188         return len;
189 }