linux/drivers/media/usb/pvrusb2/pvrusb2-ioread.c
<<
>>
Prefs
   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
  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 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
  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 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
  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 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
 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 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
 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 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