Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / media / dvb / dvb-core / dvb_ringbuffer.c
1 /*
2  *
3  * dvb_ringbuffer.c: ring buffer implementation for the dvb driver
4  *
5  * Copyright (C) 2003 Oliver Endriss
6  * Copyright (C) 2004 Andrew de Quincey
7  *
8  * based on code originally found in av7110.c & dvb_ci.c:
9  * Copyright (C) 1999-2003 Ralph  Metzler
10  *                       & Marcus Metzler for convergence integrated media GmbH
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public License
14  * as published by the Free Software Foundation; either version 2.1
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25  */
26
27
28
29 #define __KERNEL_SYSCALLS__
30 #include <linux/errno.h>
31 #include <linux/kernel.h>
32 #include <linux/module.h>
33 #include <linux/sched.h>
34 #include <linux/string.h>
35 #include <asm/uaccess.h>
36
37 #include "dvb_ringbuffer.h"
38
39 #define PKT_READY 0
40 #define PKT_DISPOSED 1
41
42
43 void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len)
44 {
45         rbuf->pread=rbuf->pwrite=0;
46         rbuf->data=data;
47         rbuf->size=len;
48         rbuf->error=0;
49
50         init_waitqueue_head(&rbuf->queue);
51
52         spin_lock_init(&(rbuf->lock));
53 }
54
55
56
57 int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf)
58 {
59         return (rbuf->pread==rbuf->pwrite);
60 }
61
62
63
64 ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf)
65 {
66         ssize_t free;
67
68         free = rbuf->pread - rbuf->pwrite;
69         if (free <= 0)
70                 free += rbuf->size;
71         return free-1;
72 }
73
74
75
76 ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf)
77 {
78         ssize_t avail;
79
80         avail = rbuf->pwrite - rbuf->pread;
81         if (avail < 0)
82                 avail += rbuf->size;
83         return avail;
84 }
85
86
87
88 void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf)
89 {
90         rbuf->pread = rbuf->pwrite;
91         rbuf->error = 0;
92 }
93
94
95
96 void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf)
97 {
98         unsigned long flags;
99
100         spin_lock_irqsave(&rbuf->lock, flags);
101         dvb_ringbuffer_flush(rbuf);
102         spin_unlock_irqrestore(&rbuf->lock, flags);
103
104         wake_up(&rbuf->queue);
105 }
106
107
108
109 ssize_t dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len, int usermem)
110 {
111         size_t todo = len;
112         size_t split;
113
114         split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0;
115         if (split > 0) {
116                 if (!usermem)
117                         memcpy(buf, rbuf->data+rbuf->pread, split);
118                 else
119                         if (copy_to_user(buf, rbuf->data+rbuf->pread, split))
120                                 return -EFAULT;
121                 buf += split;
122                 todo -= split;
123                 rbuf->pread = 0;
124         }
125         if (!usermem)
126                 memcpy(buf, rbuf->data+rbuf->pread, todo);
127         else
128                 if (copy_to_user(buf, rbuf->data+rbuf->pread, todo))
129                         return -EFAULT;
130
131         rbuf->pread = (rbuf->pread + todo) % rbuf->size;
132
133         return len;
134 }
135
136
137
138 ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t len)
139 {
140         size_t todo = len;
141         size_t split;
142
143         split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0;
144
145         if (split > 0) {
146                 memcpy(rbuf->data+rbuf->pwrite, buf, split);
147                 buf += split;
148                 todo -= split;
149                 rbuf->pwrite = 0;
150         }
151         memcpy(rbuf->data+rbuf->pwrite, buf, todo);
152         rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size;
153
154         return len;
155 }
156
157 ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len)
158 {
159         int status;
160         ssize_t oldpwrite = rbuf->pwrite;
161
162         DVB_RINGBUFFER_WRITE_BYTE(rbuf, len >> 8);
163         DVB_RINGBUFFER_WRITE_BYTE(rbuf, len & 0xff);
164         DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_READY);
165         status = dvb_ringbuffer_write(rbuf, buf, len);
166
167         if (status < 0) rbuf->pwrite = oldpwrite;
168         return status;
169 }
170
171 ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
172                                 int offset, u8* buf, size_t len, int usermem)
173 {
174         size_t todo;
175         size_t split;
176         size_t pktlen;
177
178         pktlen = rbuf->data[idx] << 8;
179         pktlen |= rbuf->data[(idx + 1) % rbuf->size];
180         if (offset > pktlen) return -EINVAL;
181         if ((offset + len) > pktlen) len = pktlen - offset;
182
183         idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size;
184         todo = len;
185         split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0;
186         if (split > 0) {
187                 if (!usermem)
188                         memcpy(buf, rbuf->data+idx, split);
189                 else
190                         if (copy_to_user(buf, rbuf->data+idx, split))
191                                 return -EFAULT;
192                 buf += split;
193                 todo -= split;
194                 idx = 0;
195         }
196         if (!usermem)
197                 memcpy(buf, rbuf->data+idx, todo);
198         else
199                 if (copy_to_user(buf, rbuf->data+idx, todo))
200                         return -EFAULT;
201
202         return len;
203 }
204
205 void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx)
206 {
207         size_t pktlen;
208
209         rbuf->data[(idx + 2) % rbuf->size] = PKT_DISPOSED;
210
211         // clean up disposed packets
212         while(dvb_ringbuffer_avail(rbuf) > DVB_RINGBUFFER_PKTHDRSIZE) {
213                 if (DVB_RINGBUFFER_PEEK(rbuf, 2) == PKT_DISPOSED) {
214                         pktlen = DVB_RINGBUFFER_PEEK(rbuf, 0) << 8;
215                         pktlen |= DVB_RINGBUFFER_PEEK(rbuf, 1);
216                         DVB_RINGBUFFER_SKIP(rbuf, pktlen + DVB_RINGBUFFER_PKTHDRSIZE);
217                 } else {
218                         // first packet is not disposed, so we stop cleaning now
219                         break;
220                 }
221         }
222 }
223
224 ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen)
225 {
226         int consumed;
227         int curpktlen;
228         int curpktstatus;
229
230         if (idx == -1) {
231                idx = rbuf->pread;
232         } else {
233                 curpktlen = rbuf->data[idx] << 8;
234                 curpktlen |= rbuf->data[(idx + 1) % rbuf->size];
235                 idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;
236         }
237
238         consumed = (idx - rbuf->pread) % rbuf->size;
239
240         while((dvb_ringbuffer_avail(rbuf) - consumed) > DVB_RINGBUFFER_PKTHDRSIZE) {
241
242                 curpktlen = rbuf->data[idx] << 8;
243                 curpktlen |= rbuf->data[(idx + 1) % rbuf->size];
244                 curpktstatus = rbuf->data[(idx + 2) % rbuf->size];
245
246                 if (curpktstatus == PKT_READY) {
247                         *pktlen = curpktlen;
248                         return idx;
249                 }
250
251                 consumed += curpktlen + DVB_RINGBUFFER_PKTHDRSIZE;
252                 idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;
253         }
254
255         // no packets available
256         return -1;
257 }
258
259
260
261 EXPORT_SYMBOL(dvb_ringbuffer_init);
262 EXPORT_SYMBOL(dvb_ringbuffer_empty);
263 EXPORT_SYMBOL(dvb_ringbuffer_free);
264 EXPORT_SYMBOL(dvb_ringbuffer_avail);
265 EXPORT_SYMBOL(dvb_ringbuffer_flush);
266 EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup);
267 EXPORT_SYMBOL(dvb_ringbuffer_read);
268 EXPORT_SYMBOL(dvb_ringbuffer_write);
269 EXPORT_SYMBOL(dvb_ringbuffer_pkt_write);
270 EXPORT_SYMBOL(dvb_ringbuffer_pkt_read);
271 EXPORT_SYMBOL(dvb_ringbuffer_pkt_dispose);
272 EXPORT_SYMBOL(dvb_ringbuffer_pkt_next);