xref: /openbmc/linux/drivers/media/usb/pvrusb2/pvrusb2-ioread.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*2504ba9fSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20c0d06caSMauro Carvalho Chehab /*
30c0d06caSMauro Carvalho Chehab  *
40c0d06caSMauro Carvalho Chehab  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
50c0d06caSMauro Carvalho Chehab  */
60c0d06caSMauro Carvalho Chehab 
70c0d06caSMauro Carvalho Chehab #include "pvrusb2-ioread.h"
80c0d06caSMauro Carvalho Chehab #include "pvrusb2-debug.h"
90c0d06caSMauro Carvalho Chehab #include <linux/errno.h>
100c0d06caSMauro Carvalho Chehab #include <linux/string.h>
110c0d06caSMauro Carvalho Chehab #include <linux/mm.h>
120c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
130c0d06caSMauro Carvalho Chehab #include <linux/mutex.h>
147c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
150c0d06caSMauro Carvalho Chehab 
160c0d06caSMauro Carvalho Chehab #define BUFFER_COUNT 32
170c0d06caSMauro Carvalho Chehab #define BUFFER_SIZE PAGE_ALIGN(0x4000)
180c0d06caSMauro Carvalho Chehab 
190c0d06caSMauro Carvalho Chehab struct pvr2_ioread {
200c0d06caSMauro Carvalho Chehab 	struct pvr2_stream *stream;
210c0d06caSMauro Carvalho Chehab 	char *buffer_storage[BUFFER_COUNT];
220c0d06caSMauro Carvalho Chehab 	char *sync_key_ptr;
230c0d06caSMauro Carvalho Chehab 	unsigned int sync_key_len;
240c0d06caSMauro Carvalho Chehab 	unsigned int sync_buf_offs;
250c0d06caSMauro Carvalho Chehab 	unsigned int sync_state;
260c0d06caSMauro Carvalho Chehab 	unsigned int sync_trashed_count;
270c0d06caSMauro Carvalho Chehab 	int enabled;         // Streaming is on
280c0d06caSMauro Carvalho Chehab 	int spigot_open;     // OK to pass data to client
290c0d06caSMauro Carvalho Chehab 	int stream_running;  // Passing data to client now
300c0d06caSMauro Carvalho Chehab 
310c0d06caSMauro Carvalho Chehab 	/* State relevant to current buffer being read */
320c0d06caSMauro Carvalho Chehab 	struct pvr2_buffer *c_buf;
330c0d06caSMauro Carvalho Chehab 	char *c_data_ptr;
340c0d06caSMauro Carvalho Chehab 	unsigned int c_data_len;
350c0d06caSMauro Carvalho Chehab 	unsigned int c_data_offs;
360c0d06caSMauro Carvalho Chehab 	struct mutex mutex;
370c0d06caSMauro Carvalho Chehab };
380c0d06caSMauro Carvalho Chehab 
pvr2_ioread_init(struct pvr2_ioread * cp)390c0d06caSMauro Carvalho Chehab static int pvr2_ioread_init(struct pvr2_ioread *cp)
400c0d06caSMauro Carvalho Chehab {
410c0d06caSMauro Carvalho Chehab 	unsigned int idx;
420c0d06caSMauro Carvalho Chehab 
430c0d06caSMauro Carvalho Chehab 	cp->stream = NULL;
440c0d06caSMauro Carvalho Chehab 	mutex_init(&cp->mutex);
450c0d06caSMauro Carvalho Chehab 
460c0d06caSMauro Carvalho Chehab 	for (idx = 0; idx < BUFFER_COUNT; idx++) {
470c0d06caSMauro Carvalho Chehab 		cp->buffer_storage[idx] = kmalloc(BUFFER_SIZE,GFP_KERNEL);
480c0d06caSMauro Carvalho Chehab 		if (!(cp->buffer_storage[idx])) break;
490c0d06caSMauro Carvalho Chehab 	}
500c0d06caSMauro Carvalho Chehab 
510c0d06caSMauro Carvalho Chehab 	if (idx < BUFFER_COUNT) {
520c0d06caSMauro Carvalho Chehab 		// An allocation appears to have failed
530c0d06caSMauro Carvalho Chehab 		for (idx = 0; idx < BUFFER_COUNT; idx++) {
540c0d06caSMauro Carvalho Chehab 			if (!(cp->buffer_storage[idx])) continue;
550c0d06caSMauro Carvalho Chehab 			kfree(cp->buffer_storage[idx]);
560c0d06caSMauro Carvalho Chehab 		}
570c0d06caSMauro Carvalho Chehab 		return -ENOMEM;
580c0d06caSMauro Carvalho Chehab 	}
590c0d06caSMauro Carvalho Chehab 	return 0;
600c0d06caSMauro Carvalho Chehab }
610c0d06caSMauro Carvalho Chehab 
pvr2_ioread_done(struct pvr2_ioread * cp)620c0d06caSMauro Carvalho Chehab static void pvr2_ioread_done(struct pvr2_ioread *cp)
630c0d06caSMauro Carvalho Chehab {
640c0d06caSMauro Carvalho Chehab 	unsigned int idx;
650c0d06caSMauro Carvalho Chehab 
660c0d06caSMauro Carvalho Chehab 	pvr2_ioread_setup(cp,NULL);
670c0d06caSMauro Carvalho Chehab 	for (idx = 0; idx < BUFFER_COUNT; idx++) {
680c0d06caSMauro Carvalho Chehab 		if (!(cp->buffer_storage[idx])) continue;
690c0d06caSMauro Carvalho Chehab 		kfree(cp->buffer_storage[idx]);
700c0d06caSMauro Carvalho Chehab 	}
710c0d06caSMauro Carvalho Chehab }
720c0d06caSMauro Carvalho Chehab 
pvr2_ioread_create(void)730c0d06caSMauro Carvalho Chehab struct pvr2_ioread *pvr2_ioread_create(void)
740c0d06caSMauro Carvalho Chehab {
750c0d06caSMauro Carvalho Chehab 	struct pvr2_ioread *cp;
760c0d06caSMauro Carvalho Chehab 	cp = kzalloc(sizeof(*cp),GFP_KERNEL);
770c0d06caSMauro Carvalho Chehab 	if (!cp) return NULL;
780c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_create id=%p",cp);
790c0d06caSMauro Carvalho Chehab 	if (pvr2_ioread_init(cp) < 0) {
800c0d06caSMauro Carvalho Chehab 		kfree(cp);
810c0d06caSMauro Carvalho Chehab 		return NULL;
820c0d06caSMauro Carvalho Chehab 	}
830c0d06caSMauro Carvalho Chehab 	return cp;
840c0d06caSMauro Carvalho Chehab }
850c0d06caSMauro Carvalho Chehab 
pvr2_ioread_destroy(struct pvr2_ioread * cp)860c0d06caSMauro Carvalho Chehab void pvr2_ioread_destroy(struct pvr2_ioread *cp)
870c0d06caSMauro Carvalho Chehab {
880c0d06caSMauro Carvalho Chehab 	if (!cp) return;
890c0d06caSMauro Carvalho Chehab 	pvr2_ioread_done(cp);
900c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_destroy id=%p",cp);
910c0d06caSMauro Carvalho Chehab 	if (cp->sync_key_ptr) {
920c0d06caSMauro Carvalho Chehab 		kfree(cp->sync_key_ptr);
930c0d06caSMauro Carvalho Chehab 		cp->sync_key_ptr = NULL;
940c0d06caSMauro Carvalho Chehab 	}
950c0d06caSMauro Carvalho Chehab 	kfree(cp);
960c0d06caSMauro Carvalho Chehab }
970c0d06caSMauro Carvalho Chehab 
pvr2_ioread_set_sync_key(struct pvr2_ioread * cp,const char * sync_key_ptr,unsigned int sync_key_len)980c0d06caSMauro Carvalho Chehab void pvr2_ioread_set_sync_key(struct pvr2_ioread *cp,
990c0d06caSMauro Carvalho Chehab 			      const char *sync_key_ptr,
1000c0d06caSMauro Carvalho Chehab 			      unsigned int sync_key_len)
1010c0d06caSMauro Carvalho Chehab {
1020c0d06caSMauro Carvalho Chehab 	if (!cp) return;
1030c0d06caSMauro Carvalho Chehab 
1040c0d06caSMauro Carvalho Chehab 	if (!sync_key_ptr) sync_key_len = 0;
1050c0d06caSMauro Carvalho Chehab 	if ((sync_key_len == cp->sync_key_len) &&
1060c0d06caSMauro Carvalho Chehab 	    ((!sync_key_len) ||
1070c0d06caSMauro Carvalho Chehab 	     (!memcmp(sync_key_ptr,cp->sync_key_ptr,sync_key_len)))) return;
1080c0d06caSMauro Carvalho Chehab 
1090c0d06caSMauro Carvalho Chehab 	if (sync_key_len != cp->sync_key_len) {
1100c0d06caSMauro Carvalho Chehab 		if (cp->sync_key_ptr) {
1110c0d06caSMauro Carvalho Chehab 			kfree(cp->sync_key_ptr);
1120c0d06caSMauro Carvalho Chehab 			cp->sync_key_ptr = NULL;
1130c0d06caSMauro Carvalho Chehab 		}
1140c0d06caSMauro Carvalho Chehab 		cp->sync_key_len = 0;
1150c0d06caSMauro Carvalho Chehab 		if (sync_key_len) {
1160c0d06caSMauro Carvalho Chehab 			cp->sync_key_ptr = kmalloc(sync_key_len,GFP_KERNEL);
1170c0d06caSMauro Carvalho Chehab 			if (cp->sync_key_ptr) {
1180c0d06caSMauro Carvalho Chehab 				cp->sync_key_len = sync_key_len;
1190c0d06caSMauro Carvalho Chehab 			}
1200c0d06caSMauro Carvalho Chehab 		}
1210c0d06caSMauro Carvalho Chehab 	}
1220c0d06caSMauro Carvalho Chehab 	if (!cp->sync_key_len) return;
1230c0d06caSMauro Carvalho Chehab 	memcpy(cp->sync_key_ptr,sync_key_ptr,cp->sync_key_len);
1240c0d06caSMauro Carvalho Chehab }
1250c0d06caSMauro Carvalho Chehab 
pvr2_ioread_stop(struct pvr2_ioread * cp)1260c0d06caSMauro Carvalho Chehab static void pvr2_ioread_stop(struct pvr2_ioread *cp)
1270c0d06caSMauro Carvalho Chehab {
1280c0d06caSMauro Carvalho Chehab 	if (!(cp->enabled)) return;
1290c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_START_STOP,
1300c0d06caSMauro Carvalho Chehab 		   "/*---TRACE_READ---*/ pvr2_ioread_stop id=%p",cp);
1310c0d06caSMauro Carvalho Chehab 	pvr2_stream_kill(cp->stream);
1320c0d06caSMauro Carvalho Chehab 	cp->c_buf = NULL;
1330c0d06caSMauro Carvalho Chehab 	cp->c_data_ptr = NULL;
1340c0d06caSMauro Carvalho Chehab 	cp->c_data_len = 0;
1350c0d06caSMauro Carvalho Chehab 	cp->c_data_offs = 0;
1360c0d06caSMauro Carvalho Chehab 	cp->enabled = 0;
1370c0d06caSMauro Carvalho Chehab 	cp->stream_running = 0;
1380c0d06caSMauro Carvalho Chehab 	cp->spigot_open = 0;
1390c0d06caSMauro Carvalho Chehab 	if (cp->sync_state) {
1400c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_DATA_FLOW,
1410c0d06caSMauro Carvalho Chehab 			   "/*---TRACE_READ---*/ sync_state <== 0");
1420c0d06caSMauro Carvalho Chehab 		cp->sync_state = 0;
1430c0d06caSMauro Carvalho Chehab 	}
1440c0d06caSMauro Carvalho Chehab }
1450c0d06caSMauro Carvalho Chehab 
pvr2_ioread_start(struct pvr2_ioread * cp)1460c0d06caSMauro Carvalho Chehab static int pvr2_ioread_start(struct pvr2_ioread *cp)
1470c0d06caSMauro Carvalho Chehab {
1480c0d06caSMauro Carvalho Chehab 	int stat;
1490c0d06caSMauro Carvalho Chehab 	struct pvr2_buffer *bp;
1500c0d06caSMauro Carvalho Chehab 	if (cp->enabled) return 0;
1510c0d06caSMauro Carvalho Chehab 	if (!(cp->stream)) return 0;
1520c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_START_STOP,
1530c0d06caSMauro Carvalho Chehab 		   "/*---TRACE_READ---*/ pvr2_ioread_start id=%p",cp);
1540c0d06caSMauro Carvalho Chehab 	while ((bp = pvr2_stream_get_idle_buffer(cp->stream)) != NULL) {
1550c0d06caSMauro Carvalho Chehab 		stat = pvr2_buffer_queue(bp);
1560c0d06caSMauro Carvalho Chehab 		if (stat < 0) {
1570c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_DATA_FLOW,
15896292c89SMauro Carvalho Chehab 				   "/*---TRACE_READ---*/ pvr2_ioread_start id=%p error=%d",
1590c0d06caSMauro Carvalho Chehab 				   cp,stat);
1600c0d06caSMauro Carvalho Chehab 			pvr2_ioread_stop(cp);
1610c0d06caSMauro Carvalho Chehab 			return stat;
1620c0d06caSMauro Carvalho Chehab 		}
1630c0d06caSMauro Carvalho Chehab 	}
1640c0d06caSMauro Carvalho Chehab 	cp->enabled = !0;
1650c0d06caSMauro Carvalho Chehab 	cp->c_buf = NULL;
1660c0d06caSMauro Carvalho Chehab 	cp->c_data_ptr = NULL;
1670c0d06caSMauro Carvalho Chehab 	cp->c_data_len = 0;
1680c0d06caSMauro Carvalho Chehab 	cp->c_data_offs = 0;
1690c0d06caSMauro Carvalho Chehab 	cp->stream_running = 0;
1700c0d06caSMauro Carvalho Chehab 	if (cp->sync_key_len) {
1710c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_DATA_FLOW,
1720c0d06caSMauro Carvalho Chehab 			   "/*---TRACE_READ---*/ sync_state <== 1");
1730c0d06caSMauro Carvalho Chehab 		cp->sync_state = 1;
1740c0d06caSMauro Carvalho Chehab 		cp->sync_trashed_count = 0;
1750c0d06caSMauro Carvalho Chehab 		cp->sync_buf_offs = 0;
1760c0d06caSMauro Carvalho Chehab 	}
1770c0d06caSMauro Carvalho Chehab 	cp->spigot_open = 0;
1780c0d06caSMauro Carvalho Chehab 	return 0;
1790c0d06caSMauro Carvalho Chehab }
1800c0d06caSMauro Carvalho Chehab 
pvr2_ioread_get_stream(struct pvr2_ioread * cp)1810c0d06caSMauro Carvalho Chehab struct pvr2_stream *pvr2_ioread_get_stream(struct pvr2_ioread *cp)
1820c0d06caSMauro Carvalho Chehab {
1830c0d06caSMauro Carvalho Chehab 	return cp->stream;
1840c0d06caSMauro Carvalho Chehab }
1850c0d06caSMauro Carvalho Chehab 
pvr2_ioread_setup(struct pvr2_ioread * cp,struct pvr2_stream * sp)1860c0d06caSMauro Carvalho Chehab int pvr2_ioread_setup(struct pvr2_ioread *cp,struct pvr2_stream *sp)
1870c0d06caSMauro Carvalho Chehab {
1880c0d06caSMauro Carvalho Chehab 	int ret;
1890c0d06caSMauro Carvalho Chehab 	unsigned int idx;
1900c0d06caSMauro Carvalho Chehab 	struct pvr2_buffer *bp;
1910c0d06caSMauro Carvalho Chehab 
192f419edd4SMauro Carvalho Chehab 	mutex_lock(&cp->mutex);
193f419edd4SMauro Carvalho Chehab 	do {
1940c0d06caSMauro Carvalho Chehab 		if (cp->stream) {
1950c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_START_STOP,
19696292c89SMauro Carvalho Chehab 				   "/*---TRACE_READ---*/ pvr2_ioread_setup (tear-down) id=%p",
19796292c89SMauro Carvalho Chehab 				   cp);
1980c0d06caSMauro Carvalho Chehab 			pvr2_ioread_stop(cp);
1990c0d06caSMauro Carvalho Chehab 			pvr2_stream_kill(cp->stream);
2000c0d06caSMauro Carvalho Chehab 			if (pvr2_stream_get_buffer_count(cp->stream)) {
2010c0d06caSMauro Carvalho Chehab 				pvr2_stream_set_buffer_count(cp->stream,0);
2020c0d06caSMauro Carvalho Chehab 			}
2030c0d06caSMauro Carvalho Chehab 			cp->stream = NULL;
2040c0d06caSMauro Carvalho Chehab 		}
2050c0d06caSMauro Carvalho Chehab 		if (sp) {
2060c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_START_STOP,
20796292c89SMauro Carvalho Chehab 				   "/*---TRACE_READ---*/ pvr2_ioread_setup (setup) id=%p",
20896292c89SMauro Carvalho Chehab 				   cp);
2090c0d06caSMauro Carvalho Chehab 			pvr2_stream_kill(sp);
2100c0d06caSMauro Carvalho Chehab 			ret = pvr2_stream_set_buffer_count(sp,BUFFER_COUNT);
2110c0d06caSMauro Carvalho Chehab 			if (ret < 0) {
2120c0d06caSMauro Carvalho Chehab 				mutex_unlock(&cp->mutex);
2130c0d06caSMauro Carvalho Chehab 				return ret;
2140c0d06caSMauro Carvalho Chehab 			}
2150c0d06caSMauro Carvalho Chehab 			for (idx = 0; idx < BUFFER_COUNT; idx++) {
2160c0d06caSMauro Carvalho Chehab 				bp = pvr2_stream_get_buffer(sp,idx);
2170c0d06caSMauro Carvalho Chehab 				pvr2_buffer_set_buffer(bp,
2180c0d06caSMauro Carvalho Chehab 						       cp->buffer_storage[idx],
2190c0d06caSMauro Carvalho Chehab 						       BUFFER_SIZE);
2200c0d06caSMauro Carvalho Chehab 			}
2210c0d06caSMauro Carvalho Chehab 			cp->stream = sp;
2220c0d06caSMauro Carvalho Chehab 		}
223f419edd4SMauro Carvalho Chehab 	} while (0);
224f419edd4SMauro Carvalho Chehab 	mutex_unlock(&cp->mutex);
2250c0d06caSMauro Carvalho Chehab 
2260c0d06caSMauro Carvalho Chehab 	return 0;
2270c0d06caSMauro Carvalho Chehab }
2280c0d06caSMauro Carvalho Chehab 
pvr2_ioread_set_enabled(struct pvr2_ioread * cp,int fl)2290c0d06caSMauro Carvalho Chehab int pvr2_ioread_set_enabled(struct pvr2_ioread *cp,int fl)
2300c0d06caSMauro Carvalho Chehab {
2310c0d06caSMauro Carvalho Chehab 	int ret = 0;
2320c0d06caSMauro Carvalho Chehab 	if ((!fl) == (!(cp->enabled))) return ret;
2330c0d06caSMauro Carvalho Chehab 
234f419edd4SMauro Carvalho Chehab 	mutex_lock(&cp->mutex);
235f419edd4SMauro Carvalho Chehab 	do {
2360c0d06caSMauro Carvalho Chehab 		if (fl) {
2370c0d06caSMauro Carvalho Chehab 			ret = pvr2_ioread_start(cp);
2380c0d06caSMauro Carvalho Chehab 		} else {
2390c0d06caSMauro Carvalho Chehab 			pvr2_ioread_stop(cp);
2400c0d06caSMauro Carvalho Chehab 		}
241f419edd4SMauro Carvalho Chehab 	} while (0);
242f419edd4SMauro Carvalho Chehab 	mutex_unlock(&cp->mutex);
2430c0d06caSMauro Carvalho Chehab 	return ret;
2440c0d06caSMauro Carvalho Chehab }
2450c0d06caSMauro Carvalho Chehab 
pvr2_ioread_get_buffer(struct pvr2_ioread * cp)2460c0d06caSMauro Carvalho Chehab static int pvr2_ioread_get_buffer(struct pvr2_ioread *cp)
2470c0d06caSMauro Carvalho Chehab {
2480c0d06caSMauro Carvalho Chehab 	int stat;
2490c0d06caSMauro Carvalho Chehab 
2500c0d06caSMauro Carvalho Chehab 	while (cp->c_data_len <= cp->c_data_offs) {
2510c0d06caSMauro Carvalho Chehab 		if (cp->c_buf) {
2520c0d06caSMauro Carvalho Chehab 			// Flush out current buffer first.
2530c0d06caSMauro Carvalho Chehab 			stat = pvr2_buffer_queue(cp->c_buf);
2540c0d06caSMauro Carvalho Chehab 			if (stat < 0) {
2550c0d06caSMauro Carvalho Chehab 				// Streaming error...
2560c0d06caSMauro Carvalho Chehab 				pvr2_trace(PVR2_TRACE_DATA_FLOW,
25796292c89SMauro Carvalho Chehab 					   "/*---TRACE_READ---*/ pvr2_ioread_read id=%p queue_error=%d",
2580c0d06caSMauro Carvalho Chehab 					   cp,stat);
2590c0d06caSMauro Carvalho Chehab 				pvr2_ioread_stop(cp);
2600c0d06caSMauro Carvalho Chehab 				return 0;
2610c0d06caSMauro Carvalho Chehab 			}
2620c0d06caSMauro Carvalho Chehab 			cp->c_buf = NULL;
2630c0d06caSMauro Carvalho Chehab 			cp->c_data_ptr = NULL;
2640c0d06caSMauro Carvalho Chehab 			cp->c_data_len = 0;
2650c0d06caSMauro Carvalho Chehab 			cp->c_data_offs = 0;
2660c0d06caSMauro Carvalho Chehab 		}
2670c0d06caSMauro Carvalho Chehab 		// Now get a freshly filled buffer.
2680c0d06caSMauro Carvalho Chehab 		cp->c_buf = pvr2_stream_get_ready_buffer(cp->stream);
2690c0d06caSMauro Carvalho Chehab 		if (!cp->c_buf) break; // Nothing ready; done.
2700c0d06caSMauro Carvalho Chehab 		cp->c_data_len = pvr2_buffer_get_count(cp->c_buf);
2710c0d06caSMauro Carvalho Chehab 		if (!cp->c_data_len) {
2720c0d06caSMauro Carvalho Chehab 			// Nothing transferred.  Was there an error?
2730c0d06caSMauro Carvalho Chehab 			stat = pvr2_buffer_get_status(cp->c_buf);
2740c0d06caSMauro Carvalho Chehab 			if (stat < 0) {
2750c0d06caSMauro Carvalho Chehab 				// Streaming error...
2760c0d06caSMauro Carvalho Chehab 				pvr2_trace(PVR2_TRACE_DATA_FLOW,
27796292c89SMauro Carvalho Chehab 					   "/*---TRACE_READ---*/ pvr2_ioread_read id=%p buffer_error=%d",
2780c0d06caSMauro Carvalho Chehab 					   cp,stat);
2790c0d06caSMauro Carvalho Chehab 				pvr2_ioread_stop(cp);
2800c0d06caSMauro Carvalho Chehab 				// Give up.
2810c0d06caSMauro Carvalho Chehab 				return 0;
2820c0d06caSMauro Carvalho Chehab 			}
2830c0d06caSMauro Carvalho Chehab 			// Start over...
2840c0d06caSMauro Carvalho Chehab 			continue;
2850c0d06caSMauro Carvalho Chehab 		}
2860c0d06caSMauro Carvalho Chehab 		cp->c_data_offs = 0;
2870c0d06caSMauro Carvalho Chehab 		cp->c_data_ptr = cp->buffer_storage[
2880c0d06caSMauro Carvalho Chehab 			pvr2_buffer_get_id(cp->c_buf)];
2890c0d06caSMauro Carvalho Chehab 	}
2900c0d06caSMauro Carvalho Chehab 	return !0;
2910c0d06caSMauro Carvalho Chehab }
2920c0d06caSMauro Carvalho Chehab 
pvr2_ioread_filter(struct pvr2_ioread * cp)2930c0d06caSMauro Carvalho Chehab static void pvr2_ioread_filter(struct pvr2_ioread *cp)
2940c0d06caSMauro Carvalho Chehab {
2950c0d06caSMauro Carvalho Chehab 	unsigned int idx;
2960c0d06caSMauro Carvalho Chehab 	if (!cp->enabled) return;
2970c0d06caSMauro Carvalho Chehab 	if (cp->sync_state != 1) return;
2980c0d06caSMauro Carvalho Chehab 
2990c0d06caSMauro Carvalho Chehab 	// Search the stream for our synchronization key.  This is made
3000c0d06caSMauro Carvalho Chehab 	// complicated by the fact that in order to be honest with
3010c0d06caSMauro Carvalho Chehab 	// ourselves here we must search across buffer boundaries...
302f419edd4SMauro Carvalho Chehab 	mutex_lock(&cp->mutex);
303f419edd4SMauro Carvalho Chehab 	while (1) {
3040c0d06caSMauro Carvalho Chehab 		// Ensure we have a buffer
3050c0d06caSMauro Carvalho Chehab 		if (!pvr2_ioread_get_buffer(cp)) break;
3060c0d06caSMauro Carvalho Chehab 		if (!cp->c_data_len) break;
3070c0d06caSMauro Carvalho Chehab 
3080c0d06caSMauro Carvalho Chehab 		// Now walk the buffer contents until we match the key or
3090c0d06caSMauro Carvalho Chehab 		// run out of buffer data.
3100c0d06caSMauro Carvalho Chehab 		for (idx = cp->c_data_offs; idx < cp->c_data_len; idx++) {
3110c0d06caSMauro Carvalho Chehab 			if (cp->sync_buf_offs >= cp->sync_key_len) break;
3120c0d06caSMauro Carvalho Chehab 			if (cp->c_data_ptr[idx] ==
3130c0d06caSMauro Carvalho Chehab 			    cp->sync_key_ptr[cp->sync_buf_offs]) {
3140c0d06caSMauro Carvalho Chehab 				// Found the next key byte
3150c0d06caSMauro Carvalho Chehab 				(cp->sync_buf_offs)++;
3160c0d06caSMauro Carvalho Chehab 			} else {
3170c0d06caSMauro Carvalho Chehab 				// Whoops, mismatched.  Start key over...
3180c0d06caSMauro Carvalho Chehab 				cp->sync_buf_offs = 0;
3190c0d06caSMauro Carvalho Chehab 			}
3200c0d06caSMauro Carvalho Chehab 		}
3210c0d06caSMauro Carvalho Chehab 
3220c0d06caSMauro Carvalho Chehab 		// Consume what we've walked through
3230c0d06caSMauro Carvalho Chehab 		cp->c_data_offs += idx;
3240c0d06caSMauro Carvalho Chehab 		cp->sync_trashed_count += idx;
3250c0d06caSMauro Carvalho Chehab 
3260c0d06caSMauro Carvalho Chehab 		// If we've found the key, then update state and get out.
3270c0d06caSMauro Carvalho Chehab 		if (cp->sync_buf_offs >= cp->sync_key_len) {
3280c0d06caSMauro Carvalho Chehab 			cp->sync_trashed_count -= cp->sync_key_len;
3290c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_DATA_FLOW,
33096292c89SMauro Carvalho Chehab 				   "/*---TRACE_READ---*/ sync_state <== 2 (skipped %u bytes)",
3310c0d06caSMauro Carvalho Chehab 				   cp->sync_trashed_count);
3320c0d06caSMauro Carvalho Chehab 			cp->sync_state = 2;
3330c0d06caSMauro Carvalho Chehab 			cp->sync_buf_offs = 0;
3340c0d06caSMauro Carvalho Chehab 			break;
3350c0d06caSMauro Carvalho Chehab 		}
3360c0d06caSMauro Carvalho Chehab 
3370c0d06caSMauro Carvalho Chehab 		if (cp->c_data_offs < cp->c_data_len) {
3380c0d06caSMauro Carvalho Chehab 			// Sanity check - should NEVER get here
3390c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
34096292c89SMauro Carvalho Chehab 				   "ERROR: pvr2_ioread filter sync problem len=%u offs=%u",
3410c0d06caSMauro Carvalho Chehab 				   cp->c_data_len,cp->c_data_offs);
3420c0d06caSMauro Carvalho Chehab 			// Get out so we don't get stuck in an infinite
3430c0d06caSMauro Carvalho Chehab 			// loop.
3440c0d06caSMauro Carvalho Chehab 			break;
3450c0d06caSMauro Carvalho Chehab 		}
3460c0d06caSMauro Carvalho Chehab 
3470c0d06caSMauro Carvalho Chehab 		continue; // (for clarity)
348f419edd4SMauro Carvalho Chehab 	}
349f419edd4SMauro Carvalho Chehab 	mutex_unlock(&cp->mutex);
3500c0d06caSMauro Carvalho Chehab }
3510c0d06caSMauro Carvalho Chehab 
pvr2_ioread_avail(struct pvr2_ioread * cp)3520c0d06caSMauro Carvalho Chehab int pvr2_ioread_avail(struct pvr2_ioread *cp)
3530c0d06caSMauro Carvalho Chehab {
3540c0d06caSMauro Carvalho Chehab 	int ret;
3550c0d06caSMauro Carvalho Chehab 	if (!(cp->enabled)) {
3560c0d06caSMauro Carvalho Chehab 		// Stream is not enabled; so this is an I/O error
3570c0d06caSMauro Carvalho Chehab 		return -EIO;
3580c0d06caSMauro Carvalho Chehab 	}
3590c0d06caSMauro Carvalho Chehab 
3600c0d06caSMauro Carvalho Chehab 	if (cp->sync_state == 1) {
3610c0d06caSMauro Carvalho Chehab 		pvr2_ioread_filter(cp);
3620c0d06caSMauro Carvalho Chehab 		if (cp->sync_state == 1) return -EAGAIN;
3630c0d06caSMauro Carvalho Chehab 	}
3640c0d06caSMauro Carvalho Chehab 
3650c0d06caSMauro Carvalho Chehab 	ret = 0;
3660c0d06caSMauro Carvalho Chehab 	if (cp->stream_running) {
3670c0d06caSMauro Carvalho Chehab 		if (!pvr2_stream_get_ready_count(cp->stream)) {
3680c0d06caSMauro Carvalho Chehab 			// No data available at all right now.
3690c0d06caSMauro Carvalho Chehab 			ret = -EAGAIN;
3700c0d06caSMauro Carvalho Chehab 		}
3710c0d06caSMauro Carvalho Chehab 	} else {
3720c0d06caSMauro Carvalho Chehab 		if (pvr2_stream_get_ready_count(cp->stream) < BUFFER_COUNT/2) {
3730c0d06caSMauro Carvalho Chehab 			// Haven't buffered up enough yet; try again later
3740c0d06caSMauro Carvalho Chehab 			ret = -EAGAIN;
3750c0d06caSMauro Carvalho Chehab 		}
3760c0d06caSMauro Carvalho Chehab 	}
3770c0d06caSMauro Carvalho Chehab 
3780c0d06caSMauro Carvalho Chehab 	if ((!(cp->spigot_open)) != (!(ret == 0))) {
3790c0d06caSMauro Carvalho Chehab 		cp->spigot_open = (ret == 0);
3800c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_DATA_FLOW,
3810c0d06caSMauro Carvalho Chehab 			   "/*---TRACE_READ---*/ data is %s",
3820c0d06caSMauro Carvalho Chehab 			   cp->spigot_open ? "available" : "pending");
3830c0d06caSMauro Carvalho Chehab 	}
3840c0d06caSMauro Carvalho Chehab 
3850c0d06caSMauro Carvalho Chehab 	return ret;
3860c0d06caSMauro Carvalho Chehab }
3870c0d06caSMauro Carvalho Chehab 
pvr2_ioread_read(struct pvr2_ioread * cp,void __user * buf,unsigned int cnt)3880c0d06caSMauro Carvalho Chehab int pvr2_ioread_read(struct pvr2_ioread *cp,void __user *buf,unsigned int cnt)
3890c0d06caSMauro Carvalho Chehab {
3900c0d06caSMauro Carvalho Chehab 	unsigned int copied_cnt;
3910c0d06caSMauro Carvalho Chehab 	unsigned int bcnt;
3920c0d06caSMauro Carvalho Chehab 	const char *src;
3930c0d06caSMauro Carvalho Chehab 	int stat;
3940c0d06caSMauro Carvalho Chehab 	int ret = 0;
3950c0d06caSMauro Carvalho Chehab 	unsigned int req_cnt = cnt;
3960c0d06caSMauro Carvalho Chehab 
3970c0d06caSMauro Carvalho Chehab 	if (!cnt) {
3980c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_TRAP,
39996292c89SMauro Carvalho Chehab 			   "/*---TRACE_READ---*/ pvr2_ioread_read id=%p ZERO Request? Returning zero.",
40096292c89SMauro Carvalho Chehab cp);
4010c0d06caSMauro Carvalho Chehab 		return 0;
4020c0d06caSMauro Carvalho Chehab 	}
4030c0d06caSMauro Carvalho Chehab 
4040c0d06caSMauro Carvalho Chehab 	stat = pvr2_ioread_avail(cp);
4050c0d06caSMauro Carvalho Chehab 	if (stat < 0) return stat;
4060c0d06caSMauro Carvalho Chehab 
4070c0d06caSMauro Carvalho Chehab 	cp->stream_running = !0;
4080c0d06caSMauro Carvalho Chehab 
409f419edd4SMauro Carvalho Chehab 	mutex_lock(&cp->mutex);
410f419edd4SMauro Carvalho Chehab 	do {
4110c0d06caSMauro Carvalho Chehab 
4120c0d06caSMauro Carvalho Chehab 		// Suck data out of the buffers and copy to the user
4130c0d06caSMauro Carvalho Chehab 		copied_cnt = 0;
4140c0d06caSMauro Carvalho Chehab 		if (!buf) cnt = 0;
4150c0d06caSMauro Carvalho Chehab 		while (1) {
4160c0d06caSMauro Carvalho Chehab 			if (!pvr2_ioread_get_buffer(cp)) {
4170c0d06caSMauro Carvalho Chehab 				ret = -EIO;
4180c0d06caSMauro Carvalho Chehab 				break;
4190c0d06caSMauro Carvalho Chehab 			}
4200c0d06caSMauro Carvalho Chehab 
4210c0d06caSMauro Carvalho Chehab 			if (!cnt) break;
4220c0d06caSMauro Carvalho Chehab 
4230c0d06caSMauro Carvalho Chehab 			if (cp->sync_state == 2) {
4240c0d06caSMauro Carvalho Chehab 				// We're repeating the sync key data into
4250c0d06caSMauro Carvalho Chehab 				// the stream.
4260c0d06caSMauro Carvalho Chehab 				src = cp->sync_key_ptr + cp->sync_buf_offs;
4270c0d06caSMauro Carvalho Chehab 				bcnt = cp->sync_key_len - cp->sync_buf_offs;
4280c0d06caSMauro Carvalho Chehab 			} else {
4290c0d06caSMauro Carvalho Chehab 				// Normal buffer copy
4300c0d06caSMauro Carvalho Chehab 				src = cp->c_data_ptr + cp->c_data_offs;
4310c0d06caSMauro Carvalho Chehab 				bcnt = cp->c_data_len - cp->c_data_offs;
4320c0d06caSMauro Carvalho Chehab 			}
4330c0d06caSMauro Carvalho Chehab 
4340c0d06caSMauro Carvalho Chehab 			if (!bcnt) break;
4350c0d06caSMauro Carvalho Chehab 
4360c0d06caSMauro Carvalho Chehab 			// Don't run past user's buffer
4370c0d06caSMauro Carvalho Chehab 			if (bcnt > cnt) bcnt = cnt;
4380c0d06caSMauro Carvalho Chehab 
4390c0d06caSMauro Carvalho Chehab 			if (copy_to_user(buf,src,bcnt)) {
4400c0d06caSMauro Carvalho Chehab 				// User supplied a bad pointer?
4410c0d06caSMauro Carvalho Chehab 				// Give up - this *will* cause data
4420c0d06caSMauro Carvalho Chehab 				// to be lost.
4430c0d06caSMauro Carvalho Chehab 				ret = -EFAULT;
4440c0d06caSMauro Carvalho Chehab 				break;
4450c0d06caSMauro Carvalho Chehab 			}
4460c0d06caSMauro Carvalho Chehab 			cnt -= bcnt;
4470c0d06caSMauro Carvalho Chehab 			buf += bcnt;
4480c0d06caSMauro Carvalho Chehab 			copied_cnt += bcnt;
4490c0d06caSMauro Carvalho Chehab 
4500c0d06caSMauro Carvalho Chehab 			if (cp->sync_state == 2) {
4510c0d06caSMauro Carvalho Chehab 				// Update offset inside sync key that we're
4520c0d06caSMauro Carvalho Chehab 				// repeating back out.
4530c0d06caSMauro Carvalho Chehab 				cp->sync_buf_offs += bcnt;
4540c0d06caSMauro Carvalho Chehab 				if (cp->sync_buf_offs >= cp->sync_key_len) {
4550c0d06caSMauro Carvalho Chehab 					// Consumed entire key; switch mode
4560c0d06caSMauro Carvalho Chehab 					// to normal.
4570c0d06caSMauro Carvalho Chehab 					pvr2_trace(PVR2_TRACE_DATA_FLOW,
45896292c89SMauro Carvalho Chehab 						   "/*---TRACE_READ---*/ sync_state <== 0");
4590c0d06caSMauro Carvalho Chehab 					cp->sync_state = 0;
4600c0d06caSMauro Carvalho Chehab 				}
4610c0d06caSMauro Carvalho Chehab 			} else {
4620c0d06caSMauro Carvalho Chehab 				// Update buffer offset.
4630c0d06caSMauro Carvalho Chehab 				cp->c_data_offs += bcnt;
4640c0d06caSMauro Carvalho Chehab 			}
4650c0d06caSMauro Carvalho Chehab 		}
4660c0d06caSMauro Carvalho Chehab 
467f419edd4SMauro Carvalho Chehab 	} while (0);
468f419edd4SMauro Carvalho Chehab 	mutex_unlock(&cp->mutex);
4690c0d06caSMauro Carvalho Chehab 
4700c0d06caSMauro Carvalho Chehab 	if (!ret) {
4710c0d06caSMauro Carvalho Chehab 		if (copied_cnt) {
4720c0d06caSMauro Carvalho Chehab 			// If anything was copied, return that count
4730c0d06caSMauro Carvalho Chehab 			ret = copied_cnt;
4740c0d06caSMauro Carvalho Chehab 		} else {
4750c0d06caSMauro Carvalho Chehab 			// Nothing copied; suggest to caller that another
4760c0d06caSMauro Carvalho Chehab 			// attempt should be tried again later
4770c0d06caSMauro Carvalho Chehab 			ret = -EAGAIN;
4780c0d06caSMauro Carvalho Chehab 		}
4790c0d06caSMauro Carvalho Chehab 	}
4800c0d06caSMauro Carvalho Chehab 
4810c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_DATA_FLOW,
48296292c89SMauro Carvalho Chehab 		   "/*---TRACE_READ---*/ pvr2_ioread_read id=%p request=%d result=%d",
4830c0d06caSMauro Carvalho Chehab 		   cp,req_cnt,ret);
4840c0d06caSMauro Carvalho Chehab 	return ret;
4850c0d06caSMauro Carvalho Chehab }
486