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