pvrusb2-ioread.c (11567B)
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}