Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | ||
1da177e4 LT |
29 | #include <linux/errno.h> |
30 | #include <linux/kernel.h> | |
31 | #include <linux/module.h> | |
32 | #include <linux/sched.h> | |
33 | #include <linux/string.h> | |
34 | #include <asm/uaccess.h> | |
35 | ||
36 | #include "dvb_ringbuffer.h" | |
37 | ||
38 | #define PKT_READY 0 | |
39 | #define PKT_DISPOSED 1 | |
40 | ||
41 | ||
42 | void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len) | |
43 | { | |
afd1a0c9 MCC |
44 | rbuf->pread=rbuf->pwrite=0; |
45 | rbuf->data=data; | |
46 | rbuf->size=len; | |
9d9d6bae | 47 | rbuf->error=0; |
1da177e4 | 48 | |
afd1a0c9 | 49 | init_waitqueue_head(&rbuf->queue); |
1da177e4 | 50 | |
afd1a0c9 | 51 | spin_lock_init(&(rbuf->lock)); |
1da177e4 LT |
52 | } |
53 | ||
54 | ||
55 | ||
56 | int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf) | |
57 | { | |
afd1a0c9 | 58 | return (rbuf->pread==rbuf->pwrite); |
1da177e4 LT |
59 | } |
60 | ||
61 | ||
62 | ||
63 | ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf) | |
64 | { | |
afd1a0c9 | 65 | ssize_t free; |
1da177e4 | 66 | |
afd1a0c9 MCC |
67 | free = rbuf->pread - rbuf->pwrite; |
68 | if (free <= 0) | |
69 | free += rbuf->size; | |
70 | return free-1; | |
1da177e4 LT |
71 | } |
72 | ||
73 | ||
74 | ||
75 | ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf) | |
76 | { | |
afd1a0c9 | 77 | ssize_t avail; |
1da177e4 | 78 | |
afd1a0c9 MCC |
79 | avail = rbuf->pwrite - rbuf->pread; |
80 | if (avail < 0) | |
81 | avail += rbuf->size; | |
82 | return avail; | |
1da177e4 LT |
83 | } |
84 | ||
85 | ||
86 | ||
87 | void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf) | |
88 | { | |
4304954e | 89 | rbuf->pread = rbuf->pwrite; |
9d9d6bae | 90 | rbuf->error = 0; |
1da177e4 | 91 | } |
126cd4bc | 92 | EXPORT_SYMBOL(dvb_ringbuffer_flush); |
1da177e4 | 93 | |
48c01a9c AO |
94 | void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf) |
95 | { | |
96 | rbuf->pread = rbuf->pwrite = 0; | |
97 | rbuf->error = 0; | |
98 | } | |
1da177e4 LT |
99 | |
100 | void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf) | |
101 | { | |
afd1a0c9 | 102 | unsigned long flags; |
1da177e4 | 103 | |
afd1a0c9 MCC |
104 | spin_lock_irqsave(&rbuf->lock, flags); |
105 | dvb_ringbuffer_flush(rbuf); | |
106 | spin_unlock_irqrestore(&rbuf->lock, flags); | |
1da177e4 | 107 | |
afd1a0c9 | 108 | wake_up(&rbuf->queue); |
1da177e4 LT |
109 | } |
110 | ||
b0ba0e3a | 111 | ssize_t dvb_ringbuffer_read_user(struct dvb_ringbuffer *rbuf, u8 __user *buf, size_t len) |
1da177e4 | 112 | { |
afd1a0c9 MCC |
113 | size_t todo = len; |
114 | size_t split; | |
115 | ||
116 | split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0; | |
117 | if (split > 0) { | |
b0ba0e3a AV |
118 | if (copy_to_user(buf, rbuf->data+rbuf->pread, split)) |
119 | return -EFAULT; | |
afd1a0c9 MCC |
120 | buf += split; |
121 | todo -= split; | |
122 | rbuf->pread = 0; | |
123 | } | |
b0ba0e3a AV |
124 | if (copy_to_user(buf, rbuf->data+rbuf->pread, todo)) |
125 | return -EFAULT; | |
afd1a0c9 MCC |
126 | |
127 | rbuf->pread = (rbuf->pread + todo) % rbuf->size; | |
128 | ||
129 | return len; | |
1da177e4 LT |
130 | } |
131 | ||
b0ba0e3a AV |
132 | void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len) |
133 | { | |
134 | size_t todo = len; | |
135 | size_t split; | |
136 | ||
137 | split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0; | |
138 | if (split > 0) { | |
139 | memcpy(buf, rbuf->data+rbuf->pread, split); | |
140 | buf += split; | |
141 | todo -= split; | |
142 | rbuf->pread = 0; | |
143 | } | |
144 | memcpy(buf, rbuf->data+rbuf->pread, todo); | |
145 | ||
146 | rbuf->pread = (rbuf->pread + todo) % rbuf->size; | |
147 | } | |
1da177e4 LT |
148 | |
149 | ||
150 | ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t len) | |
151 | { | |
afd1a0c9 MCC |
152 | size_t todo = len; |
153 | size_t split; | |
1da177e4 | 154 | |
afd1a0c9 | 155 | split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0; |
1da177e4 | 156 | |
afd1a0c9 MCC |
157 | if (split > 0) { |
158 | memcpy(rbuf->data+rbuf->pwrite, buf, split); | |
159 | buf += split; | |
160 | todo -= split; | |
161 | rbuf->pwrite = 0; | |
162 | } | |
163 | memcpy(rbuf->data+rbuf->pwrite, buf, todo); | |
164 | rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size; | |
1da177e4 | 165 | |
afd1a0c9 | 166 | return len; |
1da177e4 LT |
167 | } |
168 | ||
04da2dae MCC |
169 | ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf, |
170 | const u8 __user *buf, size_t len) | |
171 | { | |
172 | int status; | |
173 | size_t todo = len; | |
174 | size_t split; | |
175 | ||
176 | split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0; | |
177 | ||
178 | if (split > 0) { | |
179 | status = copy_from_user(rbuf->data+rbuf->pwrite, buf, split); | |
180 | if (status) | |
181 | return len - todo; | |
182 | buf += split; | |
183 | todo -= split; | |
184 | rbuf->pwrite = 0; | |
185 | } | |
186 | status = copy_from_user(rbuf->data+rbuf->pwrite, buf, todo); | |
187 | if (status) | |
188 | return len - todo; | |
189 | rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size; | |
190 | ||
191 | return len; | |
192 | } | |
193 | ||
1da177e4 LT |
194 | ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len) |
195 | { | |
afd1a0c9 MCC |
196 | int status; |
197 | ssize_t oldpwrite = rbuf->pwrite; | |
1da177e4 | 198 | |
afd1a0c9 MCC |
199 | DVB_RINGBUFFER_WRITE_BYTE(rbuf, len >> 8); |
200 | DVB_RINGBUFFER_WRITE_BYTE(rbuf, len & 0xff); | |
201 | DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_READY); | |
202 | status = dvb_ringbuffer_write(rbuf, buf, len); | |
1da177e4 | 203 | |
afd1a0c9 MCC |
204 | if (status < 0) rbuf->pwrite = oldpwrite; |
205 | return status; | |
1da177e4 LT |
206 | } |
207 | ||
b0ba0e3a AV |
208 | ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx, |
209 | int offset, u8 __user *buf, size_t len) | |
1da177e4 | 210 | { |
afd1a0c9 MCC |
211 | size_t todo; |
212 | size_t split; | |
213 | size_t pktlen; | |
214 | ||
215 | pktlen = rbuf->data[idx] << 8; | |
216 | pktlen |= rbuf->data[(idx + 1) % rbuf->size]; | |
217 | if (offset > pktlen) return -EINVAL; | |
218 | if ((offset + len) > pktlen) len = pktlen - offset; | |
219 | ||
220 | idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size; | |
221 | todo = len; | |
222 | split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0; | |
223 | if (split > 0) { | |
b0ba0e3a AV |
224 | if (copy_to_user(buf, rbuf->data+idx, split)) |
225 | return -EFAULT; | |
afd1a0c9 MCC |
226 | buf += split; |
227 | todo -= split; | |
228 | idx = 0; | |
229 | } | |
b0ba0e3a AV |
230 | if (copy_to_user(buf, rbuf->data+idx, todo)) |
231 | return -EFAULT; | |
232 | ||
233 | return len; | |
234 | } | |
afd1a0c9 | 235 | |
b0ba0e3a AV |
236 | ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx, |
237 | int offset, u8* buf, size_t len) | |
238 | { | |
239 | size_t todo; | |
240 | size_t split; | |
241 | size_t pktlen; | |
242 | ||
243 | pktlen = rbuf->data[idx] << 8; | |
244 | pktlen |= rbuf->data[(idx + 1) % rbuf->size]; | |
245 | if (offset > pktlen) return -EINVAL; | |
246 | if ((offset + len) > pktlen) len = pktlen - offset; | |
247 | ||
248 | idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size; | |
249 | todo = len; | |
250 | split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0; | |
251 | if (split > 0) { | |
252 | memcpy(buf, rbuf->data+idx, split); | |
253 | buf += split; | |
254 | todo -= split; | |
255 | idx = 0; | |
256 | } | |
257 | memcpy(buf, rbuf->data+idx, todo); | |
afd1a0c9 | 258 | return len; |
1da177e4 LT |
259 | } |
260 | ||
261 | void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx) | |
262 | { | |
afd1a0c9 MCC |
263 | size_t pktlen; |
264 | ||
265 | rbuf->data[(idx + 2) % rbuf->size] = PKT_DISPOSED; | |
266 | ||
267 | // clean up disposed packets | |
268 | while(dvb_ringbuffer_avail(rbuf) > DVB_RINGBUFFER_PKTHDRSIZE) { | |
269 | if (DVB_RINGBUFFER_PEEK(rbuf, 2) == PKT_DISPOSED) { | |
50c25fff MK |
270 | pktlen = DVB_RINGBUFFER_PEEK(rbuf, 0) << 8; |
271 | pktlen |= DVB_RINGBUFFER_PEEK(rbuf, 1); | |
272 | DVB_RINGBUFFER_SKIP(rbuf, pktlen + DVB_RINGBUFFER_PKTHDRSIZE); | |
afd1a0c9 | 273 | } else { |
50c25fff MK |
274 | // first packet is not disposed, so we stop cleaning now |
275 | break; | |
afd1a0c9 MCC |
276 | } |
277 | } | |
1da177e4 LT |
278 | } |
279 | ||
280 | ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen) | |
281 | { | |
afd1a0c9 MCC |
282 | int consumed; |
283 | int curpktlen; | |
284 | int curpktstatus; | |
1da177e4 | 285 | |
afd1a0c9 | 286 | if (idx == -1) { |
1da177e4 LT |
287 | idx = rbuf->pread; |
288 | } else { | |
afd1a0c9 MCC |
289 | curpktlen = rbuf->data[idx] << 8; |
290 | curpktlen |= rbuf->data[(idx + 1) % rbuf->size]; | |
291 | idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size; | |
1da177e4 LT |
292 | } |
293 | ||
afd1a0c9 | 294 | consumed = (idx - rbuf->pread) % rbuf->size; |
1da177e4 | 295 | |
afd1a0c9 | 296 | while((dvb_ringbuffer_avail(rbuf) - consumed) > DVB_RINGBUFFER_PKTHDRSIZE) { |
1da177e4 | 297 | |
afd1a0c9 MCC |
298 | curpktlen = rbuf->data[idx] << 8; |
299 | curpktlen |= rbuf->data[(idx + 1) % rbuf->size]; | |
300 | curpktstatus = rbuf->data[(idx + 2) % rbuf->size]; | |
1da177e4 | 301 | |
afd1a0c9 | 302 | if (curpktstatus == PKT_READY) { |
50c25fff MK |
303 | *pktlen = curpktlen; |
304 | return idx; | |
afd1a0c9 | 305 | } |
1da177e4 | 306 | |
afd1a0c9 MCC |
307 | consumed += curpktlen + DVB_RINGBUFFER_PKTHDRSIZE; |
308 | idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size; | |
309 | } | |
1da177e4 | 310 | |
afd1a0c9 MCC |
311 | // no packets available |
312 | return -1; | |
1da177e4 LT |
313 | } |
314 | ||
315 | ||
316 | ||
317 | EXPORT_SYMBOL(dvb_ringbuffer_init); | |
318 | EXPORT_SYMBOL(dvb_ringbuffer_empty); | |
319 | EXPORT_SYMBOL(dvb_ringbuffer_free); | |
320 | EXPORT_SYMBOL(dvb_ringbuffer_avail); | |
1da177e4 | 321 | EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup); |
b0ba0e3a | 322 | EXPORT_SYMBOL(dvb_ringbuffer_read_user); |
1da177e4 LT |
323 | EXPORT_SYMBOL(dvb_ringbuffer_read); |
324 | EXPORT_SYMBOL(dvb_ringbuffer_write); | |
04da2dae | 325 | EXPORT_SYMBOL(dvb_ringbuffer_write_user); |