diff options
Diffstat (limited to 'drivers/media/video/pvrusb2/pvrusb2-ioread.c')
-rw-r--r-- | drivers/media/video/pvrusb2/pvrusb2-ioread.c | 513 |
1 files changed, 513 insertions, 0 deletions
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.c b/drivers/media/video/pvrusb2/pvrusb2-ioread.c new file mode 100644 index 000000000000..49da062e3271 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-ioread.c | |||
@@ -0,0 +1,513 @@ | |||
1 | /* | ||
2 | * | ||
3 | * $Id$ | ||
4 | * | ||
5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License | ||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "pvrusb2-ioread.h" | ||
23 | #include "pvrusb2-debug.h" | ||
24 | #include <linux/errno.h> | ||
25 | #include <linux/string.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/mutex.h> | ||
28 | #include <asm/uaccess.h> | ||
29 | |||
30 | #define BUFFER_COUNT 32 | ||
31 | #define BUFFER_SIZE PAGE_ALIGN(0x4000) | ||
32 | |||
33 | struct pvr2_ioread { | ||
34 | struct pvr2_stream *stream; | ||
35 | char *buffer_storage[BUFFER_COUNT]; | ||
36 | char *sync_key_ptr; | ||
37 | unsigned int sync_key_len; | ||
38 | unsigned int sync_buf_offs; | ||
39 | unsigned int sync_state; | ||
40 | unsigned int sync_trashed_count; | ||
41 | int enabled; // Streaming is on | ||
42 | int spigot_open; // OK to pass data to client | ||
43 | int stream_running; // Passing data to client now | ||
44 | |||
45 | /* State relevant to current buffer being read */ | ||
46 | struct pvr2_buffer *c_buf; | ||
47 | char *c_data_ptr; | ||
48 | unsigned int c_data_len; | ||
49 | unsigned int c_data_offs; | ||
50 | struct mutex mutex; | ||
51 | }; | ||
52 | |||
53 | static int pvr2_ioread_init(struct pvr2_ioread *cp) | ||
54 | { | ||
55 | unsigned int idx; | ||
56 | |||
57 | cp->stream = 0; | ||
58 | mutex_init(&cp->mutex); | ||
59 | |||
60 | for (idx = 0; idx < BUFFER_COUNT; idx++) { | ||
61 | cp->buffer_storage[idx] = kmalloc(BUFFER_SIZE,GFP_KERNEL); | ||
62 | if (!(cp->buffer_storage[idx])) break; | ||
63 | } | ||
64 | |||
65 | if (idx < BUFFER_COUNT) { | ||
66 | // An allocation appears to have failed | ||
67 | for (idx = 0; idx < BUFFER_COUNT; idx++) { | ||
68 | if (!(cp->buffer_storage[idx])) continue; | ||
69 | kfree(cp->buffer_storage[idx]); | ||
70 | } | ||
71 | return -ENOMEM; | ||
72 | } | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | static void pvr2_ioread_done(struct pvr2_ioread *cp) | ||
77 | { | ||
78 | unsigned int idx; | ||
79 | |||
80 | pvr2_ioread_setup(cp,0); | ||
81 | for (idx = 0; idx < BUFFER_COUNT; idx++) { | ||
82 | if (!(cp->buffer_storage[idx])) continue; | ||
83 | kfree(cp->buffer_storage[idx]); | ||
84 | } | ||
85 | } | ||
86 | |||
87 | struct pvr2_ioread *pvr2_ioread_create(void) | ||
88 | { | ||
89 | struct pvr2_ioread *cp; | ||
90 | cp = kmalloc(sizeof(*cp),GFP_KERNEL); | ||
91 | if (!cp) return 0; | ||
92 | pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_create id=%p",cp); | ||
93 | memset(cp,0,sizeof(*cp)); | ||
94 | if (pvr2_ioread_init(cp) < 0) { | ||
95 | kfree(cp); | ||
96 | return 0; | ||
97 | } | ||
98 | return cp; | ||
99 | } | ||
100 | |||
101 | void pvr2_ioread_destroy(struct pvr2_ioread *cp) | ||
102 | { | ||
103 | if (!cp) return; | ||
104 | pvr2_ioread_done(cp); | ||
105 | pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_destroy id=%p",cp); | ||
106 | if (cp->sync_key_ptr) { | ||
107 | kfree(cp->sync_key_ptr); | ||
108 | cp->sync_key_ptr = 0; | ||
109 | } | ||
110 | kfree(cp); | ||
111 | } | ||
112 | |||
113 | void pvr2_ioread_set_sync_key(struct pvr2_ioread *cp, | ||
114 | const char *sync_key_ptr, | ||
115 | unsigned int sync_key_len) | ||
116 | { | ||
117 | if (!cp) return; | ||
118 | |||
119 | if (!sync_key_ptr) sync_key_len = 0; | ||
120 | if ((sync_key_len == cp->sync_key_len) && | ||
121 | ((!sync_key_len) || | ||
122 | (!memcmp(sync_key_ptr,cp->sync_key_ptr,sync_key_len)))) return; | ||
123 | |||
124 | if (sync_key_len != cp->sync_key_len) { | ||
125 | if (cp->sync_key_ptr) { | ||
126 | kfree(cp->sync_key_ptr); | ||
127 | cp->sync_key_ptr = 0; | ||
128 | } | ||
129 | cp->sync_key_len = 0; | ||
130 | if (sync_key_len) { | ||
131 | cp->sync_key_ptr = kmalloc(sync_key_len,GFP_KERNEL); | ||
132 | if (cp->sync_key_ptr) { | ||
133 | cp->sync_key_len = sync_key_len; | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | if (!cp->sync_key_len) return; | ||
138 | memcpy(cp->sync_key_ptr,sync_key_ptr,cp->sync_key_len); | ||
139 | } | ||
140 | |||
141 | static void pvr2_ioread_stop(struct pvr2_ioread *cp) | ||
142 | { | ||
143 | if (!(cp->enabled)) return; | ||
144 | pvr2_trace(PVR2_TRACE_START_STOP, | ||
145 | "/*---TRACE_READ---*/ pvr2_ioread_stop id=%p",cp); | ||
146 | pvr2_stream_kill(cp->stream); | ||
147 | cp->c_buf = 0; | ||
148 | cp->c_data_ptr = 0; | ||
149 | cp->c_data_len = 0; | ||
150 | cp->c_data_offs = 0; | ||
151 | cp->enabled = 0; | ||
152 | cp->stream_running = 0; | ||
153 | cp->spigot_open = 0; | ||
154 | if (cp->sync_state) { | ||
155 | pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||
156 | "/*---TRACE_READ---*/ sync_state <== 0"); | ||
157 | cp->sync_state = 0; | ||
158 | } | ||
159 | } | ||
160 | |||
161 | static int pvr2_ioread_start(struct pvr2_ioread *cp) | ||
162 | { | ||
163 | int stat; | ||
164 | struct pvr2_buffer *bp; | ||
165 | if (cp->enabled) return 0; | ||
166 | if (!(cp->stream)) return 0; | ||
167 | pvr2_trace(PVR2_TRACE_START_STOP, | ||
168 | "/*---TRACE_READ---*/ pvr2_ioread_start id=%p",cp); | ||
169 | while ((bp = pvr2_stream_get_idle_buffer(cp->stream)) != 0) { | ||
170 | stat = pvr2_buffer_queue(bp); | ||
171 | if (stat < 0) { | ||
172 | pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||
173 | "/*---TRACE_READ---*/" | ||
174 | " pvr2_ioread_start id=%p" | ||
175 | " error=%d", | ||
176 | cp,stat); | ||
177 | pvr2_ioread_stop(cp); | ||
178 | return stat; | ||
179 | } | ||
180 | } | ||
181 | cp->enabled = !0; | ||
182 | cp->c_buf = 0; | ||
183 | cp->c_data_ptr = 0; | ||
184 | cp->c_data_len = 0; | ||
185 | cp->c_data_offs = 0; | ||
186 | cp->stream_running = 0; | ||
187 | if (cp->sync_key_len) { | ||
188 | pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||
189 | "/*---TRACE_READ---*/ sync_state <== 1"); | ||
190 | cp->sync_state = 1; | ||
191 | cp->sync_trashed_count = 0; | ||
192 | cp->sync_buf_offs = 0; | ||
193 | } | ||
194 | cp->spigot_open = 0; | ||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | struct pvr2_stream *pvr2_ioread_get_stream(struct pvr2_ioread *cp) | ||
199 | { | ||
200 | return cp->stream; | ||
201 | } | ||
202 | |||
203 | int pvr2_ioread_setup(struct pvr2_ioread *cp,struct pvr2_stream *sp) | ||
204 | { | ||
205 | int ret; | ||
206 | unsigned int idx; | ||
207 | struct pvr2_buffer *bp; | ||
208 | |||
209 | mutex_lock(&cp->mutex); do { | ||
210 | if (cp->stream) { | ||
211 | pvr2_trace(PVR2_TRACE_START_STOP, | ||
212 | "/*---TRACE_READ---*/" | ||
213 | " pvr2_ioread_setup (tear-down) id=%p",cp); | ||
214 | pvr2_ioread_stop(cp); | ||
215 | pvr2_stream_kill(cp->stream); | ||
216 | pvr2_stream_set_buffer_count(cp->stream,0); | ||
217 | cp->stream = 0; | ||
218 | } | ||
219 | if (sp) { | ||
220 | pvr2_trace(PVR2_TRACE_START_STOP, | ||
221 | "/*---TRACE_READ---*/" | ||
222 | " pvr2_ioread_setup (setup) id=%p",cp); | ||
223 | pvr2_stream_kill(sp); | ||
224 | ret = pvr2_stream_set_buffer_count(sp,BUFFER_COUNT); | ||
225 | if (ret < 0) return ret; | ||
226 | for (idx = 0; idx < BUFFER_COUNT; idx++) { | ||
227 | bp = pvr2_stream_get_buffer(sp,idx); | ||
228 | pvr2_buffer_set_buffer(bp, | ||
229 | cp->buffer_storage[idx], | ||
230 | BUFFER_SIZE); | ||
231 | } | ||
232 | cp->stream = sp; | ||
233 | } | ||
234 | } while (0); mutex_unlock(&cp->mutex); | ||
235 | |||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | int pvr2_ioread_set_enabled(struct pvr2_ioread *cp,int fl) | ||
240 | { | ||
241 | int ret = 0; | ||
242 | if ((!fl) == (!(cp->enabled))) return ret; | ||
243 | |||
244 | mutex_lock(&cp->mutex); do { | ||
245 | if (fl) { | ||
246 | ret = pvr2_ioread_start(cp); | ||
247 | } else { | ||
248 | pvr2_ioread_stop(cp); | ||
249 | } | ||
250 | } while (0); mutex_unlock(&cp->mutex); | ||
251 | return ret; | ||
252 | } | ||
253 | |||
254 | int pvr2_ioread_get_enabled(struct pvr2_ioread *cp) | ||
255 | { | ||
256 | return cp->enabled != 0; | ||
257 | } | ||
258 | |||
259 | int pvr2_ioread_get_buffer(struct pvr2_ioread *cp) | ||
260 | { | ||
261 | int stat; | ||
262 | |||
263 | while (cp->c_data_len <= cp->c_data_offs) { | ||
264 | if (cp->c_buf) { | ||
265 | // Flush out current buffer first. | ||
266 | stat = pvr2_buffer_queue(cp->c_buf); | ||
267 | if (stat < 0) { | ||
268 | // Streaming error... | ||
269 | pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||
270 | "/*---TRACE_READ---*/" | ||
271 | " pvr2_ioread_read id=%p" | ||
272 | " queue_error=%d", | ||
273 | cp,stat); | ||
274 | pvr2_ioread_stop(cp); | ||
275 | return 0; | ||
276 | } | ||
277 | cp->c_buf = 0; | ||
278 | cp->c_data_ptr = 0; | ||
279 | cp->c_data_len = 0; | ||
280 | cp->c_data_offs = 0; | ||
281 | } | ||
282 | // Now get a freshly filled buffer. | ||
283 | cp->c_buf = pvr2_stream_get_ready_buffer(cp->stream); | ||
284 | if (!cp->c_buf) break; // Nothing ready; done. | ||
285 | cp->c_data_len = pvr2_buffer_get_count(cp->c_buf); | ||
286 | if (!cp->c_data_len) { | ||
287 | // Nothing transferred. Was there an error? | ||
288 | stat = pvr2_buffer_get_status(cp->c_buf); | ||
289 | if (stat < 0) { | ||
290 | // Streaming error... | ||
291 | pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||
292 | "/*---TRACE_READ---*/" | ||
293 | " pvr2_ioread_read id=%p" | ||
294 | " buffer_error=%d", | ||
295 | cp,stat); | ||
296 | pvr2_ioread_stop(cp); | ||
297 | // Give up. | ||
298 | return 0; | ||
299 | } | ||
300 | // Start over... | ||
301 | continue; | ||
302 | } | ||
303 | cp->c_data_offs = 0; | ||
304 | cp->c_data_ptr = cp->buffer_storage[ | ||
305 | pvr2_buffer_get_id(cp->c_buf)]; | ||
306 | } | ||
307 | return !0; | ||
308 | } | ||
309 | |||
310 | void pvr2_ioread_filter(struct pvr2_ioread *cp) | ||
311 | { | ||
312 | unsigned int idx; | ||
313 | if (!cp->enabled) return; | ||
314 | if (cp->sync_state != 1) return; | ||
315 | |||
316 | // Search the stream for our synchronization key. This is made | ||
317 | // complicated by the fact that in order to be honest with | ||
318 | // ourselves here we must search across buffer boundaries... | ||
319 | mutex_lock(&cp->mutex); while (1) { | ||
320 | // Ensure we have a buffer | ||
321 | if (!pvr2_ioread_get_buffer(cp)) break; | ||
322 | if (!cp->c_data_len) break; | ||
323 | |||
324 | // Now walk the buffer contents until we match the key or | ||
325 | // run out of buffer data. | ||
326 | for (idx = cp->c_data_offs; idx < cp->c_data_len; idx++) { | ||
327 | if (cp->sync_buf_offs >= cp->sync_key_len) break; | ||
328 | if (cp->c_data_ptr[idx] == | ||
329 | cp->sync_key_ptr[cp->sync_buf_offs]) { | ||
330 | // Found the next key byte | ||
331 | (cp->sync_buf_offs)++; | ||
332 | } else { | ||
333 | // Whoops, mismatched. Start key over... | ||
334 | cp->sync_buf_offs = 0; | ||
335 | } | ||
336 | } | ||
337 | |||
338 | // Consume what we've walked through | ||
339 | cp->c_data_offs += idx; | ||
340 | cp->sync_trashed_count += idx; | ||
341 | |||
342 | // If we've found the key, then update state and get out. | ||
343 | if (cp->sync_buf_offs >= cp->sync_key_len) { | ||
344 | cp->sync_trashed_count -= cp->sync_key_len; | ||
345 | pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||
346 | "/*---TRACE_READ---*/" | ||
347 | " sync_state <== 2 (skipped %u bytes)", | ||
348 | cp->sync_trashed_count); | ||
349 | cp->sync_state = 2; | ||
350 | cp->sync_buf_offs = 0; | ||
351 | break; | ||
352 | } | ||
353 | |||
354 | if (cp->c_data_offs < cp->c_data_len) { | ||
355 | // Sanity check - should NEVER get here | ||
356 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
357 | "ERROR: pvr2_ioread filter sync problem" | ||
358 | " len=%u offs=%u", | ||
359 | cp->c_data_len,cp->c_data_offs); | ||
360 | // Get out so we don't get stuck in an infinite | ||
361 | // loop. | ||
362 | break; | ||
363 | } | ||
364 | |||
365 | continue; // (for clarity) | ||
366 | } mutex_unlock(&cp->mutex); | ||
367 | } | ||
368 | |||
369 | int pvr2_ioread_avail(struct pvr2_ioread *cp) | ||
370 | { | ||
371 | int ret; | ||
372 | if (!(cp->enabled)) { | ||
373 | // Stream is not enabled; so this is an I/O error | ||
374 | return -EIO; | ||
375 | } | ||
376 | |||
377 | if (cp->sync_state == 1) { | ||
378 | pvr2_ioread_filter(cp); | ||
379 | if (cp->sync_state == 1) return -EAGAIN; | ||
380 | } | ||
381 | |||
382 | ret = 0; | ||
383 | if (cp->stream_running) { | ||
384 | if (!pvr2_stream_get_ready_count(cp->stream)) { | ||
385 | // No data available at all right now. | ||
386 | ret = -EAGAIN; | ||
387 | } | ||
388 | } else { | ||
389 | if (pvr2_stream_get_ready_count(cp->stream) < BUFFER_COUNT/2) { | ||
390 | // Haven't buffered up enough yet; try again later | ||
391 | ret = -EAGAIN; | ||
392 | } | ||
393 | } | ||
394 | |||
395 | if ((!(cp->spigot_open)) != (!(ret == 0))) { | ||
396 | cp->spigot_open = (ret == 0); | ||
397 | pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||
398 | "/*---TRACE_READ---*/ data is %s", | ||
399 | cp->spigot_open ? "available" : "pending"); | ||
400 | } | ||
401 | |||
402 | return ret; | ||
403 | } | ||
404 | |||
405 | int pvr2_ioread_read(struct pvr2_ioread *cp,void __user *buf,unsigned int cnt) | ||
406 | { | ||
407 | unsigned int copied_cnt; | ||
408 | unsigned int bcnt; | ||
409 | const char *src; | ||
410 | int stat; | ||
411 | int ret = 0; | ||
412 | unsigned int req_cnt = cnt; | ||
413 | |||
414 | if (!cnt) { | ||
415 | pvr2_trace(PVR2_TRACE_TRAP, | ||
416 | "/*---TRACE_READ---*/ pvr2_ioread_read id=%p" | ||
417 | " ZERO Request? Returning zero.",cp); | ||
418 | return 0; | ||
419 | } | ||
420 | |||
421 | stat = pvr2_ioread_avail(cp); | ||
422 | if (stat < 0) return stat; | ||
423 | |||
424 | cp->stream_running = !0; | ||
425 | |||
426 | mutex_lock(&cp->mutex); do { | ||
427 | |||
428 | // Suck data out of the buffers and copy to the user | ||
429 | copied_cnt = 0; | ||
430 | if (!buf) cnt = 0; | ||
431 | while (1) { | ||
432 | if (!pvr2_ioread_get_buffer(cp)) { | ||
433 | ret = -EIO; | ||
434 | break; | ||
435 | } | ||
436 | |||
437 | if (!cnt) break; | ||
438 | |||
439 | if (cp->sync_state == 2) { | ||
440 | // We're repeating the sync key data into | ||
441 | // the stream. | ||
442 | src = cp->sync_key_ptr + cp->sync_buf_offs; | ||
443 | bcnt = cp->sync_key_len - cp->sync_buf_offs; | ||
444 | } else { | ||
445 | // Normal buffer copy | ||
446 | src = cp->c_data_ptr + cp->c_data_offs; | ||
447 | bcnt = cp->c_data_len - cp->c_data_offs; | ||
448 | } | ||
449 | |||
450 | if (!bcnt) break; | ||
451 | |||
452 | // Don't run past user's buffer | ||
453 | if (bcnt > cnt) bcnt = cnt; | ||
454 | |||
455 | if (copy_to_user(buf,src,bcnt)) { | ||
456 | // User supplied a bad pointer? | ||
457 | // Give up - this *will* cause data | ||
458 | // to be lost. | ||
459 | ret = -EFAULT; | ||
460 | break; | ||
461 | } | ||
462 | cnt -= bcnt; | ||
463 | buf += bcnt; | ||
464 | copied_cnt += bcnt; | ||
465 | |||
466 | if (cp->sync_state == 2) { | ||
467 | // Update offset inside sync key that we're | ||
468 | // repeating back out. | ||
469 | cp->sync_buf_offs += bcnt; | ||
470 | if (cp->sync_buf_offs >= cp->sync_key_len) { | ||
471 | // Consumed entire key; switch mode | ||
472 | // to normal. | ||
473 | pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||
474 | "/*---TRACE_READ---*/" | ||
475 | " sync_state <== 0"); | ||
476 | cp->sync_state = 0; | ||
477 | } | ||
478 | } else { | ||
479 | // Update buffer offset. | ||
480 | cp->c_data_offs += bcnt; | ||
481 | } | ||
482 | } | ||
483 | |||
484 | } while (0); mutex_unlock(&cp->mutex); | ||
485 | |||
486 | if (!ret) { | ||
487 | if (copied_cnt) { | ||
488 | // If anything was copied, return that count | ||
489 | ret = copied_cnt; | ||
490 | } else { | ||
491 | // Nothing copied; suggest to caller that another | ||
492 | // attempt should be tried again later | ||
493 | ret = -EAGAIN; | ||
494 | } | ||
495 | } | ||
496 | |||
497 | pvr2_trace(PVR2_TRACE_DATA_FLOW, | ||
498 | "/*---TRACE_READ---*/ pvr2_ioread_read" | ||
499 | " id=%p request=%d result=%d", | ||
500 | cp,req_cnt,ret); | ||
501 | return ret; | ||
502 | } | ||
503 | |||
504 | |||
505 | /* | ||
506 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
507 | *** Local Variables: *** | ||
508 | *** mode: c *** | ||
509 | *** fill-column: 75 *** | ||
510 | *** tab-width: 8 *** | ||
511 | *** c-basic-offset: 8 *** | ||
512 | *** End: *** | ||
513 | */ | ||