]>
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 | ||
169 | ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len) | |
170 | { | |
afd1a0c9 MCC |
171 | int status; |
172 | ssize_t oldpwrite = rbuf->pwrite; | |
1da177e4 | 173 | |
afd1a0c9 MCC |
174 | DVB_RINGBUFFER_WRITE_BYTE(rbuf, len >> 8); |
175 | DVB_RINGBUFFER_WRITE_BYTE(rbuf, len & 0xff); | |
176 | DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_READY); | |
177 | status = dvb_ringbuffer_write(rbuf, buf, len); | |
1da177e4 | 178 | |
afd1a0c9 MCC |
179 | if (status < 0) rbuf->pwrite = oldpwrite; |
180 | return status; | |
1da177e4 LT |
181 | } |
182 | ||
b0ba0e3a AV |
183 | ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx, |
184 | int offset, u8 __user *buf, size_t len) | |
1da177e4 | 185 | { |
afd1a0c9 MCC |
186 | size_t todo; |
187 | size_t split; | |
188 | size_t pktlen; | |
189 | ||
190 | pktlen = rbuf->data[idx] << 8; | |
191 | pktlen |= rbuf->data[(idx + 1) % rbuf->size]; | |
192 | if (offset > pktlen) return -EINVAL; | |
193 | if ((offset + len) > pktlen) len = pktlen - offset; | |
194 | ||
195 | idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size; | |
196 | todo = len; | |
197 | split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0; | |
198 | if (split > 0) { | |
b0ba0e3a AV |
199 | if (copy_to_user(buf, rbuf->data+idx, split)) |
200 | return -EFAULT; | |
afd1a0c9 MCC |
201 | buf += split; |
202 | todo -= split; | |
203 | idx = 0; | |
204 | } | |
b0ba0e3a AV |
205 | if (copy_to_user(buf, rbuf->data+idx, todo)) |
206 | return -EFAULT; | |
207 | ||
208 | return len; | |
209 | } | |
afd1a0c9 | 210 | |
b0ba0e3a AV |
211 | ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx, |
212 | int offset, u8* buf, size_t len) | |
213 | { | |
214 | size_t todo; | |
215 | size_t split; | |
216 | size_t pktlen; | |
217 | ||
218 | pktlen = rbuf->data[idx] << 8; | |
219 | pktlen |= rbuf->data[(idx + 1) % rbuf->size]; | |
220 | if (offset > pktlen) return -EINVAL; | |
221 | if ((offset + len) > pktlen) len = pktlen - offset; | |
222 | ||
223 | idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size; | |
224 | todo = len; | |
225 | split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0; | |
226 | if (split > 0) { | |
227 | memcpy(buf, rbuf->data+idx, split); | |
228 | buf += split; | |
229 | todo -= split; | |
230 | idx = 0; | |
231 | } | |
232 | memcpy(buf, rbuf->data+idx, todo); | |
afd1a0c9 | 233 | return len; |
1da177e4 LT |
234 | } |
235 | ||
236 | void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx) | |
237 | { | |
afd1a0c9 MCC |
238 | size_t pktlen; |
239 | ||
240 | rbuf->data[(idx + 2) % rbuf->size] = PKT_DISPOSED; | |
241 | ||
242 | // clean up disposed packets | |
243 | while(dvb_ringbuffer_avail(rbuf) > DVB_RINGBUFFER_PKTHDRSIZE) { | |
244 | if (DVB_RINGBUFFER_PEEK(rbuf, 2) == PKT_DISPOSED) { | |
50c25fff MK |
245 | pktlen = DVB_RINGBUFFER_PEEK(rbuf, 0) << 8; |
246 | pktlen |= DVB_RINGBUFFER_PEEK(rbuf, 1); | |
247 | DVB_RINGBUFFER_SKIP(rbuf, pktlen + DVB_RINGBUFFER_PKTHDRSIZE); | |
afd1a0c9 | 248 | } else { |
50c25fff MK |
249 | // first packet is not disposed, so we stop cleaning now |
250 | break; | |
afd1a0c9 MCC |
251 | } |
252 | } | |
1da177e4 LT |
253 | } |
254 | ||
255 | ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen) | |
256 | { | |
afd1a0c9 MCC |
257 | int consumed; |
258 | int curpktlen; | |
259 | int curpktstatus; | |
1da177e4 | 260 | |
afd1a0c9 | 261 | if (idx == -1) { |
1da177e4 LT |
262 | idx = rbuf->pread; |
263 | } else { | |
afd1a0c9 MCC |
264 | curpktlen = rbuf->data[idx] << 8; |
265 | curpktlen |= rbuf->data[(idx + 1) % rbuf->size]; | |
266 | idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size; | |
1da177e4 LT |
267 | } |
268 | ||
afd1a0c9 | 269 | consumed = (idx - rbuf->pread) % rbuf->size; |
1da177e4 | 270 | |
afd1a0c9 | 271 | while((dvb_ringbuffer_avail(rbuf) - consumed) > DVB_RINGBUFFER_PKTHDRSIZE) { |
1da177e4 | 272 | |
afd1a0c9 MCC |
273 | curpktlen = rbuf->data[idx] << 8; |
274 | curpktlen |= rbuf->data[(idx + 1) % rbuf->size]; | |
275 | curpktstatus = rbuf->data[(idx + 2) % rbuf->size]; | |
1da177e4 | 276 | |
afd1a0c9 | 277 | if (curpktstatus == PKT_READY) { |
50c25fff MK |
278 | *pktlen = curpktlen; |
279 | return idx; | |
afd1a0c9 | 280 | } |
1da177e4 | 281 | |
afd1a0c9 MCC |
282 | consumed += curpktlen + DVB_RINGBUFFER_PKTHDRSIZE; |
283 | idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size; | |
284 | } | |
1da177e4 | 285 | |
afd1a0c9 MCC |
286 | // no packets available |
287 | return -1; | |
1da177e4 LT |
288 | } |
289 | ||
290 | ||
291 | ||
292 | EXPORT_SYMBOL(dvb_ringbuffer_init); | |
293 | EXPORT_SYMBOL(dvb_ringbuffer_empty); | |
294 | EXPORT_SYMBOL(dvb_ringbuffer_free); | |
295 | EXPORT_SYMBOL(dvb_ringbuffer_avail); | |
1da177e4 | 296 | EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup); |
b0ba0e3a | 297 | EXPORT_SYMBOL(dvb_ringbuffer_read_user); |
1da177e4 LT |
298 | EXPORT_SYMBOL(dvb_ringbuffer_read); |
299 | EXPORT_SYMBOL(dvb_ringbuffer_write); |