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