18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include "pvrusb2-ioread.h"
88c2ecf20Sopenharmony_ci#include "pvrusb2-debug.h"
98c2ecf20Sopenharmony_ci#include <linux/errno.h>
108c2ecf20Sopenharmony_ci#include <linux/string.h>
118c2ecf20Sopenharmony_ci#include <linux/mm.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci#include <linux/mutex.h>
148c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define BUFFER_COUNT 32
178c2ecf20Sopenharmony_ci#define BUFFER_SIZE PAGE_ALIGN(0x4000)
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistruct pvr2_ioread {
208c2ecf20Sopenharmony_ci	struct pvr2_stream *stream;
218c2ecf20Sopenharmony_ci	char *buffer_storage[BUFFER_COUNT];
228c2ecf20Sopenharmony_ci	char *sync_key_ptr;
238c2ecf20Sopenharmony_ci	unsigned int sync_key_len;
248c2ecf20Sopenharmony_ci	unsigned int sync_buf_offs;
258c2ecf20Sopenharmony_ci	unsigned int sync_state;
268c2ecf20Sopenharmony_ci	unsigned int sync_trashed_count;
278c2ecf20Sopenharmony_ci	int enabled;         // Streaming is on
288c2ecf20Sopenharmony_ci	int spigot_open;     // OK to pass data to client
298c2ecf20Sopenharmony_ci	int stream_running;  // Passing data to client now
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	/* State relevant to current buffer being read */
328c2ecf20Sopenharmony_ci	struct pvr2_buffer *c_buf;
338c2ecf20Sopenharmony_ci	char *c_data_ptr;
348c2ecf20Sopenharmony_ci	unsigned int c_data_len;
358c2ecf20Sopenharmony_ci	unsigned int c_data_offs;
368c2ecf20Sopenharmony_ci	struct mutex mutex;
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic int pvr2_ioread_init(struct pvr2_ioread *cp)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	unsigned int idx;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	cp->stream = NULL;
448c2ecf20Sopenharmony_ci	mutex_init(&cp->mutex);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	for (idx = 0; idx < BUFFER_COUNT; idx++) {
478c2ecf20Sopenharmony_ci		cp->buffer_storage[idx] = kmalloc(BUFFER_SIZE,GFP_KERNEL);
488c2ecf20Sopenharmony_ci		if (!(cp->buffer_storage[idx])) break;
498c2ecf20Sopenharmony_ci	}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	if (idx < BUFFER_COUNT) {
528c2ecf20Sopenharmony_ci		// An allocation appears to have failed
538c2ecf20Sopenharmony_ci		for (idx = 0; idx < BUFFER_COUNT; idx++) {
548c2ecf20Sopenharmony_ci			if (!(cp->buffer_storage[idx])) continue;
558c2ecf20Sopenharmony_ci			kfree(cp->buffer_storage[idx]);
568c2ecf20Sopenharmony_ci		}
578c2ecf20Sopenharmony_ci		return -ENOMEM;
588c2ecf20Sopenharmony_ci	}
598c2ecf20Sopenharmony_ci	return 0;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic void pvr2_ioread_done(struct pvr2_ioread *cp)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	unsigned int idx;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	pvr2_ioread_setup(cp,NULL);
678c2ecf20Sopenharmony_ci	for (idx = 0; idx < BUFFER_COUNT; idx++) {
688c2ecf20Sopenharmony_ci		if (!(cp->buffer_storage[idx])) continue;
698c2ecf20Sopenharmony_ci		kfree(cp->buffer_storage[idx]);
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistruct pvr2_ioread *pvr2_ioread_create(void)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct pvr2_ioread *cp;
768c2ecf20Sopenharmony_ci	cp = kzalloc(sizeof(*cp),GFP_KERNEL);
778c2ecf20Sopenharmony_ci	if (!cp) return NULL;
788c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_create id=%p",cp);
798c2ecf20Sopenharmony_ci	if (pvr2_ioread_init(cp) < 0) {
808c2ecf20Sopenharmony_ci		kfree(cp);
818c2ecf20Sopenharmony_ci		return NULL;
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci	return cp;
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_civoid pvr2_ioread_destroy(struct pvr2_ioread *cp)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	if (!cp) return;
898c2ecf20Sopenharmony_ci	pvr2_ioread_done(cp);
908c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_destroy id=%p",cp);
918c2ecf20Sopenharmony_ci	if (cp->sync_key_ptr) {
928c2ecf20Sopenharmony_ci		kfree(cp->sync_key_ptr);
938c2ecf20Sopenharmony_ci		cp->sync_key_ptr = NULL;
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci	kfree(cp);
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_civoid pvr2_ioread_set_sync_key(struct pvr2_ioread *cp,
998c2ecf20Sopenharmony_ci			      const char *sync_key_ptr,
1008c2ecf20Sopenharmony_ci			      unsigned int sync_key_len)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	if (!cp) return;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	if (!sync_key_ptr) sync_key_len = 0;
1058c2ecf20Sopenharmony_ci	if ((sync_key_len == cp->sync_key_len) &&
1068c2ecf20Sopenharmony_ci	    ((!sync_key_len) ||
1078c2ecf20Sopenharmony_ci	     (!memcmp(sync_key_ptr,cp->sync_key_ptr,sync_key_len)))) return;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	if (sync_key_len != cp->sync_key_len) {
1108c2ecf20Sopenharmony_ci		if (cp->sync_key_ptr) {
1118c2ecf20Sopenharmony_ci			kfree(cp->sync_key_ptr);
1128c2ecf20Sopenharmony_ci			cp->sync_key_ptr = NULL;
1138c2ecf20Sopenharmony_ci		}
1148c2ecf20Sopenharmony_ci		cp->sync_key_len = 0;
1158c2ecf20Sopenharmony_ci		if (sync_key_len) {
1168c2ecf20Sopenharmony_ci			cp->sync_key_ptr = kmalloc(sync_key_len,GFP_KERNEL);
1178c2ecf20Sopenharmony_ci			if (cp->sync_key_ptr) {
1188c2ecf20Sopenharmony_ci				cp->sync_key_len = sync_key_len;
1198c2ecf20Sopenharmony_ci			}
1208c2ecf20Sopenharmony_ci		}
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci	if (!cp->sync_key_len) return;
1238c2ecf20Sopenharmony_ci	memcpy(cp->sync_key_ptr,sync_key_ptr,cp->sync_key_len);
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic void pvr2_ioread_stop(struct pvr2_ioread *cp)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	if (!(cp->enabled)) return;
1298c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_START_STOP,
1308c2ecf20Sopenharmony_ci		   "/*---TRACE_READ---*/ pvr2_ioread_stop id=%p",cp);
1318c2ecf20Sopenharmony_ci	pvr2_stream_kill(cp->stream);
1328c2ecf20Sopenharmony_ci	cp->c_buf = NULL;
1338c2ecf20Sopenharmony_ci	cp->c_data_ptr = NULL;
1348c2ecf20Sopenharmony_ci	cp->c_data_len = 0;
1358c2ecf20Sopenharmony_ci	cp->c_data_offs = 0;
1368c2ecf20Sopenharmony_ci	cp->enabled = 0;
1378c2ecf20Sopenharmony_ci	cp->stream_running = 0;
1388c2ecf20Sopenharmony_ci	cp->spigot_open = 0;
1398c2ecf20Sopenharmony_ci	if (cp->sync_state) {
1408c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_DATA_FLOW,
1418c2ecf20Sopenharmony_ci			   "/*---TRACE_READ---*/ sync_state <== 0");
1428c2ecf20Sopenharmony_ci		cp->sync_state = 0;
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic int pvr2_ioread_start(struct pvr2_ioread *cp)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	int stat;
1498c2ecf20Sopenharmony_ci	struct pvr2_buffer *bp;
1508c2ecf20Sopenharmony_ci	if (cp->enabled) return 0;
1518c2ecf20Sopenharmony_ci	if (!(cp->stream)) return 0;
1528c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_START_STOP,
1538c2ecf20Sopenharmony_ci		   "/*---TRACE_READ---*/ pvr2_ioread_start id=%p",cp);
1548c2ecf20Sopenharmony_ci	while ((bp = pvr2_stream_get_idle_buffer(cp->stream)) != NULL) {
1558c2ecf20Sopenharmony_ci		stat = pvr2_buffer_queue(bp);
1568c2ecf20Sopenharmony_ci		if (stat < 0) {
1578c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_DATA_FLOW,
1588c2ecf20Sopenharmony_ci				   "/*---TRACE_READ---*/ pvr2_ioread_start id=%p error=%d",
1598c2ecf20Sopenharmony_ci				   cp,stat);
1608c2ecf20Sopenharmony_ci			pvr2_ioread_stop(cp);
1618c2ecf20Sopenharmony_ci			return stat;
1628c2ecf20Sopenharmony_ci		}
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci	cp->enabled = !0;
1658c2ecf20Sopenharmony_ci	cp->c_buf = NULL;
1668c2ecf20Sopenharmony_ci	cp->c_data_ptr = NULL;
1678c2ecf20Sopenharmony_ci	cp->c_data_len = 0;
1688c2ecf20Sopenharmony_ci	cp->c_data_offs = 0;
1698c2ecf20Sopenharmony_ci	cp->stream_running = 0;
1708c2ecf20Sopenharmony_ci	if (cp->sync_key_len) {
1718c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_DATA_FLOW,
1728c2ecf20Sopenharmony_ci			   "/*---TRACE_READ---*/ sync_state <== 1");
1738c2ecf20Sopenharmony_ci		cp->sync_state = 1;
1748c2ecf20Sopenharmony_ci		cp->sync_trashed_count = 0;
1758c2ecf20Sopenharmony_ci		cp->sync_buf_offs = 0;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci	cp->spigot_open = 0;
1788c2ecf20Sopenharmony_ci	return 0;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistruct pvr2_stream *pvr2_ioread_get_stream(struct pvr2_ioread *cp)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	return cp->stream;
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ciint pvr2_ioread_setup(struct pvr2_ioread *cp,struct pvr2_stream *sp)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	int ret;
1898c2ecf20Sopenharmony_ci	unsigned int idx;
1908c2ecf20Sopenharmony_ci	struct pvr2_buffer *bp;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	mutex_lock(&cp->mutex);
1938c2ecf20Sopenharmony_ci	do {
1948c2ecf20Sopenharmony_ci		if (cp->stream) {
1958c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_START_STOP,
1968c2ecf20Sopenharmony_ci				   "/*---TRACE_READ---*/ pvr2_ioread_setup (tear-down) id=%p",
1978c2ecf20Sopenharmony_ci				   cp);
1988c2ecf20Sopenharmony_ci			pvr2_ioread_stop(cp);
1998c2ecf20Sopenharmony_ci			pvr2_stream_kill(cp->stream);
2008c2ecf20Sopenharmony_ci			if (pvr2_stream_get_buffer_count(cp->stream)) {
2018c2ecf20Sopenharmony_ci				pvr2_stream_set_buffer_count(cp->stream,0);
2028c2ecf20Sopenharmony_ci			}
2038c2ecf20Sopenharmony_ci			cp->stream = NULL;
2048c2ecf20Sopenharmony_ci		}
2058c2ecf20Sopenharmony_ci		if (sp) {
2068c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_START_STOP,
2078c2ecf20Sopenharmony_ci				   "/*---TRACE_READ---*/ pvr2_ioread_setup (setup) id=%p",
2088c2ecf20Sopenharmony_ci				   cp);
2098c2ecf20Sopenharmony_ci			pvr2_stream_kill(sp);
2108c2ecf20Sopenharmony_ci			ret = pvr2_stream_set_buffer_count(sp,BUFFER_COUNT);
2118c2ecf20Sopenharmony_ci			if (ret < 0) {
2128c2ecf20Sopenharmony_ci				mutex_unlock(&cp->mutex);
2138c2ecf20Sopenharmony_ci				return ret;
2148c2ecf20Sopenharmony_ci			}
2158c2ecf20Sopenharmony_ci			for (idx = 0; idx < BUFFER_COUNT; idx++) {
2168c2ecf20Sopenharmony_ci				bp = pvr2_stream_get_buffer(sp,idx);
2178c2ecf20Sopenharmony_ci				pvr2_buffer_set_buffer(bp,
2188c2ecf20Sopenharmony_ci						       cp->buffer_storage[idx],
2198c2ecf20Sopenharmony_ci						       BUFFER_SIZE);
2208c2ecf20Sopenharmony_ci			}
2218c2ecf20Sopenharmony_ci			cp->stream = sp;
2228c2ecf20Sopenharmony_ci		}
2238c2ecf20Sopenharmony_ci	} while (0);
2248c2ecf20Sopenharmony_ci	mutex_unlock(&cp->mutex);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	return 0;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ciint pvr2_ioread_set_enabled(struct pvr2_ioread *cp,int fl)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	int ret = 0;
2328c2ecf20Sopenharmony_ci	if ((!fl) == (!(cp->enabled))) return ret;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	mutex_lock(&cp->mutex);
2358c2ecf20Sopenharmony_ci	do {
2368c2ecf20Sopenharmony_ci		if (fl) {
2378c2ecf20Sopenharmony_ci			ret = pvr2_ioread_start(cp);
2388c2ecf20Sopenharmony_ci		} else {
2398c2ecf20Sopenharmony_ci			pvr2_ioread_stop(cp);
2408c2ecf20Sopenharmony_ci		}
2418c2ecf20Sopenharmony_ci	} while (0);
2428c2ecf20Sopenharmony_ci	mutex_unlock(&cp->mutex);
2438c2ecf20Sopenharmony_ci	return ret;
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic int pvr2_ioread_get_buffer(struct pvr2_ioread *cp)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	int stat;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	while (cp->c_data_len <= cp->c_data_offs) {
2518c2ecf20Sopenharmony_ci		if (cp->c_buf) {
2528c2ecf20Sopenharmony_ci			// Flush out current buffer first.
2538c2ecf20Sopenharmony_ci			stat = pvr2_buffer_queue(cp->c_buf);
2548c2ecf20Sopenharmony_ci			if (stat < 0) {
2558c2ecf20Sopenharmony_ci				// Streaming error...
2568c2ecf20Sopenharmony_ci				pvr2_trace(PVR2_TRACE_DATA_FLOW,
2578c2ecf20Sopenharmony_ci					   "/*---TRACE_READ---*/ pvr2_ioread_read id=%p queue_error=%d",
2588c2ecf20Sopenharmony_ci					   cp,stat);
2598c2ecf20Sopenharmony_ci				pvr2_ioread_stop(cp);
2608c2ecf20Sopenharmony_ci				return 0;
2618c2ecf20Sopenharmony_ci			}
2628c2ecf20Sopenharmony_ci			cp->c_buf = NULL;
2638c2ecf20Sopenharmony_ci			cp->c_data_ptr = NULL;
2648c2ecf20Sopenharmony_ci			cp->c_data_len = 0;
2658c2ecf20Sopenharmony_ci			cp->c_data_offs = 0;
2668c2ecf20Sopenharmony_ci		}
2678c2ecf20Sopenharmony_ci		// Now get a freshly filled buffer.
2688c2ecf20Sopenharmony_ci		cp->c_buf = pvr2_stream_get_ready_buffer(cp->stream);
2698c2ecf20Sopenharmony_ci		if (!cp->c_buf) break; // Nothing ready; done.
2708c2ecf20Sopenharmony_ci		cp->c_data_len = pvr2_buffer_get_count(cp->c_buf);
2718c2ecf20Sopenharmony_ci		if (!cp->c_data_len) {
2728c2ecf20Sopenharmony_ci			// Nothing transferred.  Was there an error?
2738c2ecf20Sopenharmony_ci			stat = pvr2_buffer_get_status(cp->c_buf);
2748c2ecf20Sopenharmony_ci			if (stat < 0) {
2758c2ecf20Sopenharmony_ci				// Streaming error...
2768c2ecf20Sopenharmony_ci				pvr2_trace(PVR2_TRACE_DATA_FLOW,
2778c2ecf20Sopenharmony_ci					   "/*---TRACE_READ---*/ pvr2_ioread_read id=%p buffer_error=%d",
2788c2ecf20Sopenharmony_ci					   cp,stat);
2798c2ecf20Sopenharmony_ci				pvr2_ioread_stop(cp);
2808c2ecf20Sopenharmony_ci				// Give up.
2818c2ecf20Sopenharmony_ci				return 0;
2828c2ecf20Sopenharmony_ci			}
2838c2ecf20Sopenharmony_ci			// Start over...
2848c2ecf20Sopenharmony_ci			continue;
2858c2ecf20Sopenharmony_ci		}
2868c2ecf20Sopenharmony_ci		cp->c_data_offs = 0;
2878c2ecf20Sopenharmony_ci		cp->c_data_ptr = cp->buffer_storage[
2888c2ecf20Sopenharmony_ci			pvr2_buffer_get_id(cp->c_buf)];
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci	return !0;
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic void pvr2_ioread_filter(struct pvr2_ioread *cp)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	unsigned int idx;
2968c2ecf20Sopenharmony_ci	if (!cp->enabled) return;
2978c2ecf20Sopenharmony_ci	if (cp->sync_state != 1) return;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	// Search the stream for our synchronization key.  This is made
3008c2ecf20Sopenharmony_ci	// complicated by the fact that in order to be honest with
3018c2ecf20Sopenharmony_ci	// ourselves here we must search across buffer boundaries...
3028c2ecf20Sopenharmony_ci	mutex_lock(&cp->mutex);
3038c2ecf20Sopenharmony_ci	while (1) {
3048c2ecf20Sopenharmony_ci		// Ensure we have a buffer
3058c2ecf20Sopenharmony_ci		if (!pvr2_ioread_get_buffer(cp)) break;
3068c2ecf20Sopenharmony_ci		if (!cp->c_data_len) break;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci		// Now walk the buffer contents until we match the key or
3098c2ecf20Sopenharmony_ci		// run out of buffer data.
3108c2ecf20Sopenharmony_ci		for (idx = cp->c_data_offs; idx < cp->c_data_len; idx++) {
3118c2ecf20Sopenharmony_ci			if (cp->sync_buf_offs >= cp->sync_key_len) break;
3128c2ecf20Sopenharmony_ci			if (cp->c_data_ptr[idx] ==
3138c2ecf20Sopenharmony_ci			    cp->sync_key_ptr[cp->sync_buf_offs]) {
3148c2ecf20Sopenharmony_ci				// Found the next key byte
3158c2ecf20Sopenharmony_ci				(cp->sync_buf_offs)++;
3168c2ecf20Sopenharmony_ci			} else {
3178c2ecf20Sopenharmony_ci				// Whoops, mismatched.  Start key over...
3188c2ecf20Sopenharmony_ci				cp->sync_buf_offs = 0;
3198c2ecf20Sopenharmony_ci			}
3208c2ecf20Sopenharmony_ci		}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci		// Consume what we've walked through
3238c2ecf20Sopenharmony_ci		cp->c_data_offs += idx;
3248c2ecf20Sopenharmony_ci		cp->sync_trashed_count += idx;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci		// If we've found the key, then update state and get out.
3278c2ecf20Sopenharmony_ci		if (cp->sync_buf_offs >= cp->sync_key_len) {
3288c2ecf20Sopenharmony_ci			cp->sync_trashed_count -= cp->sync_key_len;
3298c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_DATA_FLOW,
3308c2ecf20Sopenharmony_ci				   "/*---TRACE_READ---*/ sync_state <== 2 (skipped %u bytes)",
3318c2ecf20Sopenharmony_ci				   cp->sync_trashed_count);
3328c2ecf20Sopenharmony_ci			cp->sync_state = 2;
3338c2ecf20Sopenharmony_ci			cp->sync_buf_offs = 0;
3348c2ecf20Sopenharmony_ci			break;
3358c2ecf20Sopenharmony_ci		}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci		if (cp->c_data_offs < cp->c_data_len) {
3388c2ecf20Sopenharmony_ci			// Sanity check - should NEVER get here
3398c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
3408c2ecf20Sopenharmony_ci				   "ERROR: pvr2_ioread filter sync problem len=%u offs=%u",
3418c2ecf20Sopenharmony_ci				   cp->c_data_len,cp->c_data_offs);
3428c2ecf20Sopenharmony_ci			// Get out so we don't get stuck in an infinite
3438c2ecf20Sopenharmony_ci			// loop.
3448c2ecf20Sopenharmony_ci			break;
3458c2ecf20Sopenharmony_ci		}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci		continue; // (for clarity)
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci	mutex_unlock(&cp->mutex);
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ciint pvr2_ioread_avail(struct pvr2_ioread *cp)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	int ret;
3558c2ecf20Sopenharmony_ci	if (!(cp->enabled)) {
3568c2ecf20Sopenharmony_ci		// Stream is not enabled; so this is an I/O error
3578c2ecf20Sopenharmony_ci		return -EIO;
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	if (cp->sync_state == 1) {
3618c2ecf20Sopenharmony_ci		pvr2_ioread_filter(cp);
3628c2ecf20Sopenharmony_ci		if (cp->sync_state == 1) return -EAGAIN;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	ret = 0;
3668c2ecf20Sopenharmony_ci	if (cp->stream_running) {
3678c2ecf20Sopenharmony_ci		if (!pvr2_stream_get_ready_count(cp->stream)) {
3688c2ecf20Sopenharmony_ci			// No data available at all right now.
3698c2ecf20Sopenharmony_ci			ret = -EAGAIN;
3708c2ecf20Sopenharmony_ci		}
3718c2ecf20Sopenharmony_ci	} else {
3728c2ecf20Sopenharmony_ci		if (pvr2_stream_get_ready_count(cp->stream) < BUFFER_COUNT/2) {
3738c2ecf20Sopenharmony_ci			// Haven't buffered up enough yet; try again later
3748c2ecf20Sopenharmony_ci			ret = -EAGAIN;
3758c2ecf20Sopenharmony_ci		}
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	if ((!(cp->spigot_open)) != (!(ret == 0))) {
3798c2ecf20Sopenharmony_ci		cp->spigot_open = (ret == 0);
3808c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_DATA_FLOW,
3818c2ecf20Sopenharmony_ci			   "/*---TRACE_READ---*/ data is %s",
3828c2ecf20Sopenharmony_ci			   cp->spigot_open ? "available" : "pending");
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	return ret;
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ciint pvr2_ioread_read(struct pvr2_ioread *cp,void __user *buf,unsigned int cnt)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	unsigned int copied_cnt;
3918c2ecf20Sopenharmony_ci	unsigned int bcnt;
3928c2ecf20Sopenharmony_ci	const char *src;
3938c2ecf20Sopenharmony_ci	int stat;
3948c2ecf20Sopenharmony_ci	int ret = 0;
3958c2ecf20Sopenharmony_ci	unsigned int req_cnt = cnt;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	if (!cnt) {
3988c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_TRAP,
3998c2ecf20Sopenharmony_ci			   "/*---TRACE_READ---*/ pvr2_ioread_read id=%p ZERO Request? Returning zero.",
4008c2ecf20Sopenharmony_cicp);
4018c2ecf20Sopenharmony_ci		return 0;
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	stat = pvr2_ioread_avail(cp);
4058c2ecf20Sopenharmony_ci	if (stat < 0) return stat;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	cp->stream_running = !0;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	mutex_lock(&cp->mutex);
4108c2ecf20Sopenharmony_ci	do {
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci		// Suck data out of the buffers and copy to the user
4138c2ecf20Sopenharmony_ci		copied_cnt = 0;
4148c2ecf20Sopenharmony_ci		if (!buf) cnt = 0;
4158c2ecf20Sopenharmony_ci		while (1) {
4168c2ecf20Sopenharmony_ci			if (!pvr2_ioread_get_buffer(cp)) {
4178c2ecf20Sopenharmony_ci				ret = -EIO;
4188c2ecf20Sopenharmony_ci				break;
4198c2ecf20Sopenharmony_ci			}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci			if (!cnt) break;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci			if (cp->sync_state == 2) {
4248c2ecf20Sopenharmony_ci				// We're repeating the sync key data into
4258c2ecf20Sopenharmony_ci				// the stream.
4268c2ecf20Sopenharmony_ci				src = cp->sync_key_ptr + cp->sync_buf_offs;
4278c2ecf20Sopenharmony_ci				bcnt = cp->sync_key_len - cp->sync_buf_offs;
4288c2ecf20Sopenharmony_ci			} else {
4298c2ecf20Sopenharmony_ci				// Normal buffer copy
4308c2ecf20Sopenharmony_ci				src = cp->c_data_ptr + cp->c_data_offs;
4318c2ecf20Sopenharmony_ci				bcnt = cp->c_data_len - cp->c_data_offs;
4328c2ecf20Sopenharmony_ci			}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci			if (!bcnt) break;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci			// Don't run past user's buffer
4378c2ecf20Sopenharmony_ci			if (bcnt > cnt) bcnt = cnt;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci			if (copy_to_user(buf,src,bcnt)) {
4408c2ecf20Sopenharmony_ci				// User supplied a bad pointer?
4418c2ecf20Sopenharmony_ci				// Give up - this *will* cause data
4428c2ecf20Sopenharmony_ci				// to be lost.
4438c2ecf20Sopenharmony_ci				ret = -EFAULT;
4448c2ecf20Sopenharmony_ci				break;
4458c2ecf20Sopenharmony_ci			}
4468c2ecf20Sopenharmony_ci			cnt -= bcnt;
4478c2ecf20Sopenharmony_ci			buf += bcnt;
4488c2ecf20Sopenharmony_ci			copied_cnt += bcnt;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci			if (cp->sync_state == 2) {
4518c2ecf20Sopenharmony_ci				// Update offset inside sync key that we're
4528c2ecf20Sopenharmony_ci				// repeating back out.
4538c2ecf20Sopenharmony_ci				cp->sync_buf_offs += bcnt;
4548c2ecf20Sopenharmony_ci				if (cp->sync_buf_offs >= cp->sync_key_len) {
4558c2ecf20Sopenharmony_ci					// Consumed entire key; switch mode
4568c2ecf20Sopenharmony_ci					// to normal.
4578c2ecf20Sopenharmony_ci					pvr2_trace(PVR2_TRACE_DATA_FLOW,
4588c2ecf20Sopenharmony_ci						   "/*---TRACE_READ---*/ sync_state <== 0");
4598c2ecf20Sopenharmony_ci					cp->sync_state = 0;
4608c2ecf20Sopenharmony_ci				}
4618c2ecf20Sopenharmony_ci			} else {
4628c2ecf20Sopenharmony_ci				// Update buffer offset.
4638c2ecf20Sopenharmony_ci				cp->c_data_offs += bcnt;
4648c2ecf20Sopenharmony_ci			}
4658c2ecf20Sopenharmony_ci		}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	} while (0);
4688c2ecf20Sopenharmony_ci	mutex_unlock(&cp->mutex);
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	if (!ret) {
4718c2ecf20Sopenharmony_ci		if (copied_cnt) {
4728c2ecf20Sopenharmony_ci			// If anything was copied, return that count
4738c2ecf20Sopenharmony_ci			ret = copied_cnt;
4748c2ecf20Sopenharmony_ci		} else {
4758c2ecf20Sopenharmony_ci			// Nothing copied; suggest to caller that another
4768c2ecf20Sopenharmony_ci			// attempt should be tried again later
4778c2ecf20Sopenharmony_ci			ret = -EAGAIN;
4788c2ecf20Sopenharmony_ci		}
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_DATA_FLOW,
4828c2ecf20Sopenharmony_ci		   "/*---TRACE_READ---*/ pvr2_ioread_read id=%p request=%d result=%d",
4838c2ecf20Sopenharmony_ci		   cp,req_cnt,ret);
4848c2ecf20Sopenharmony_ci	return ret;
4858c2ecf20Sopenharmony_ci}
486