brintos

brintos / linux-shallow public Read only

0
0
Text · 11.3 KiB · 46f8013 Raw
486 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*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 3217#define BUFFER_SIZE PAGE_ALIGN(0x4000)18 19struct 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 on28	int spigot_open;     // OK to pass data to client29	int stream_running;  // Passing data to client now30 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 39static 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 failed53		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 62static 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 73struct 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 86void 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 98void 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 126static 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 146static 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 181struct pvr2_stream *pvr2_ioread_get_stream(struct pvr2_ioread *cp)182{183	return cp->stream;184}185 186int 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 229int 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 246static 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 293static 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 made300	// complicated by the fact that in order to be honest with301	// ourselves here we must search across buffer boundaries...302	mutex_lock(&cp->mutex);303	while (1) {304		// Ensure we have a buffer305		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 or309		// 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 byte315				(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 through323		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 here339			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 infinite343			// loop.344			break;345		}346 347		continue; // (for clarity)348	}349	mutex_unlock(&cp->mutex);350}351 352int 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 error357		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 later374			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 388int 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.",400cp);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 user413		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 into425				// 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 copy430				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 buffer437			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 data442				// 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're452				// repeating back out.453				cp->sync_buf_offs += bcnt;454				if (cp->sync_buf_offs >= cp->sync_key_len) {455					// Consumed entire key; switch mode456					// 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 count473			ret = copied_cnt;474		} else {475			// Nothing copied; suggest to caller that another476			// attempt should be tried again later477			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