2006-06-26 20:58:46 -03:00
/*
*
* $ Id $
*
* Copyright ( C ) 2005 Mike Isely < isely @ pobox . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
*/
# include "pvrusb2-ioread.h"
# include "pvrusb2-debug.h"
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/slab.h>
# include <linux/mutex.h>
# include <asm/uaccess.h>
# define BUFFER_COUNT 32
# define BUFFER_SIZE PAGE_ALIGN(0x4000)
struct pvr2_ioread {
struct pvr2_stream * stream ;
char * buffer_storage [ BUFFER_COUNT ] ;
char * sync_key_ptr ;
unsigned int sync_key_len ;
unsigned int sync_buf_offs ;
unsigned int sync_state ;
unsigned int sync_trashed_count ;
int enabled ; // Streaming is on
int spigot_open ; // OK to pass data to client
int stream_running ; // Passing data to client now
/* State relevant to current buffer being read */
struct pvr2_buffer * c_buf ;
char * c_data_ptr ;
unsigned int c_data_len ;
unsigned int c_data_offs ;
struct mutex mutex ;
} ;
static int pvr2_ioread_init ( struct pvr2_ioread * cp )
{
unsigned int idx ;
2006-06-30 11:35:28 -03:00
cp - > stream = NULL ;
2006-06-26 20:58:46 -03:00
mutex_init ( & cp - > mutex ) ;
for ( idx = 0 ; idx < BUFFER_COUNT ; idx + + ) {
cp - > buffer_storage [ idx ] = kmalloc ( BUFFER_SIZE , GFP_KERNEL ) ;
if ( ! ( cp - > buffer_storage [ idx ] ) ) break ;
}
if ( idx < BUFFER_COUNT ) {
// An allocation appears to have failed
for ( idx = 0 ; idx < BUFFER_COUNT ; idx + + ) {
if ( ! ( cp - > buffer_storage [ idx ] ) ) continue ;
kfree ( cp - > buffer_storage [ idx ] ) ;
}
return - ENOMEM ;
}
return 0 ;
}
static void pvr2_ioread_done ( struct pvr2_ioread * cp )
{
unsigned int idx ;
2006-06-30 11:35:28 -03:00
pvr2_ioread_setup ( cp , NULL ) ;
2006-06-26 20:58:46 -03:00
for ( idx = 0 ; idx < BUFFER_COUNT ; idx + + ) {
if ( ! ( cp - > buffer_storage [ idx ] ) ) continue ;
kfree ( cp - > buffer_storage [ idx ] ) ;
}
}
struct pvr2_ioread * pvr2_ioread_create ( void )
{
struct pvr2_ioread * cp ;
cp = kmalloc ( sizeof ( * cp ) , GFP_KERNEL ) ;
2006-06-30 11:35:28 -03:00
if ( ! cp ) return NULL ;
2006-06-26 20:58:46 -03:00
pvr2_trace ( PVR2_TRACE_STRUCT , " pvr2_ioread_create id=%p " , cp ) ;
memset ( cp , 0 , sizeof ( * cp ) ) ;
if ( pvr2_ioread_init ( cp ) < 0 ) {
kfree ( cp ) ;
2006-06-30 11:35:28 -03:00
return NULL ;
2006-06-26 20:58:46 -03:00
}
return cp ;
}
void pvr2_ioread_destroy ( struct pvr2_ioread * cp )
{
if ( ! cp ) return ;
pvr2_ioread_done ( cp ) ;
pvr2_trace ( PVR2_TRACE_STRUCT , " pvr2_ioread_destroy id=%p " , cp ) ;
if ( cp - > sync_key_ptr ) {
kfree ( cp - > sync_key_ptr ) ;
2006-06-30 11:35:28 -03:00
cp - > sync_key_ptr = NULL ;
2006-06-26 20:58:46 -03:00
}
kfree ( cp ) ;
}
void pvr2_ioread_set_sync_key ( struct pvr2_ioread * cp ,
const char * sync_key_ptr ,
unsigned int sync_key_len )
{
if ( ! cp ) return ;
if ( ! sync_key_ptr ) sync_key_len = 0 ;
if ( ( sync_key_len = = cp - > sync_key_len ) & &
( ( ! sync_key_len ) | |
( ! memcmp ( sync_key_ptr , cp - > sync_key_ptr , sync_key_len ) ) ) ) return ;
if ( sync_key_len ! = cp - > sync_key_len ) {
if ( cp - > sync_key_ptr ) {
kfree ( cp - > sync_key_ptr ) ;
2006-06-30 11:35:28 -03:00
cp - > sync_key_ptr = NULL ;
2006-06-26 20:58:46 -03:00
}
cp - > sync_key_len = 0 ;
if ( sync_key_len ) {
cp - > sync_key_ptr = kmalloc ( sync_key_len , GFP_KERNEL ) ;
if ( cp - > sync_key_ptr ) {
cp - > sync_key_len = sync_key_len ;
}
}
}
if ( ! cp - > sync_key_len ) return ;
memcpy ( cp - > sync_key_ptr , sync_key_ptr , cp - > sync_key_len ) ;
}
static void pvr2_ioread_stop ( struct pvr2_ioread * cp )
{
if ( ! ( cp - > enabled ) ) return ;
pvr2_trace ( PVR2_TRACE_START_STOP ,
" /*---TRACE_READ---*/ pvr2_ioread_stop id=%p " , cp ) ;
pvr2_stream_kill ( cp - > stream ) ;
2006-06-30 11:35:28 -03:00
cp - > c_buf = NULL ;
cp - > c_data_ptr = NULL ;
2006-06-26 20:58:46 -03:00
cp - > c_data_len = 0 ;
cp - > c_data_offs = 0 ;
cp - > enabled = 0 ;
cp - > stream_running = 0 ;
cp - > spigot_open = 0 ;
if ( cp - > sync_state ) {
pvr2_trace ( PVR2_TRACE_DATA_FLOW ,
" /*---TRACE_READ---*/ sync_state <== 0 " ) ;
cp - > sync_state = 0 ;
}
}
static int pvr2_ioread_start ( struct pvr2_ioread * cp )
{
int stat ;
struct pvr2_buffer * bp ;
if ( cp - > enabled ) return 0 ;
if ( ! ( cp - > stream ) ) return 0 ;
pvr2_trace ( PVR2_TRACE_START_STOP ,
" /*---TRACE_READ---*/ pvr2_ioread_start id=%p " , cp ) ;
while ( ( bp = pvr2_stream_get_idle_buffer ( cp - > stream ) ) ! = 0 ) {
stat = pvr2_buffer_queue ( bp ) ;
if ( stat < 0 ) {
pvr2_trace ( PVR2_TRACE_DATA_FLOW ,
" /*---TRACE_READ---*/ "
" pvr2_ioread_start id=%p "
" error=%d " ,
cp , stat ) ;
pvr2_ioread_stop ( cp ) ;
return stat ;
}
}
cp - > enabled = ! 0 ;
2006-06-30 11:35:28 -03:00
cp - > c_buf = NULL ;
cp - > c_data_ptr = NULL ;
2006-06-26 20:58:46 -03:00
cp - > c_data_len = 0 ;
cp - > c_data_offs = 0 ;
cp - > stream_running = 0 ;
if ( cp - > sync_key_len ) {
pvr2_trace ( PVR2_TRACE_DATA_FLOW ,
" /*---TRACE_READ---*/ sync_state <== 1 " ) ;
cp - > sync_state = 1 ;
cp - > sync_trashed_count = 0 ;
cp - > sync_buf_offs = 0 ;
}
cp - > spigot_open = 0 ;
return 0 ;
}
struct pvr2_stream * pvr2_ioread_get_stream ( struct pvr2_ioread * cp )
{
return cp - > stream ;
}
int pvr2_ioread_setup ( struct pvr2_ioread * cp , struct pvr2_stream * sp )
{
int ret ;
unsigned int idx ;
struct pvr2_buffer * bp ;
mutex_lock ( & cp - > mutex ) ; do {
if ( cp - > stream ) {
pvr2_trace ( PVR2_TRACE_START_STOP ,
" /*---TRACE_READ---*/ "
" pvr2_ioread_setup (tear-down) id=%p " , cp ) ;
pvr2_ioread_stop ( cp ) ;
pvr2_stream_kill ( cp - > stream ) ;
2006-07-18 22:42:18 -03:00
if ( pvr2_stream_get_buffer_count ( cp - > stream ) ) {
pvr2_stream_set_buffer_count ( cp - > stream , 0 ) ;
}
2006-06-30 11:35:28 -03:00
cp - > stream = NULL ;
2006-06-26 20:58:46 -03:00
}
if ( sp ) {
pvr2_trace ( PVR2_TRACE_START_STOP ,
" /*---TRACE_READ---*/ "
" pvr2_ioread_setup (setup) id=%p " , cp ) ;
pvr2_stream_kill ( sp ) ;
ret = pvr2_stream_set_buffer_count ( sp , BUFFER_COUNT ) ;
if ( ret < 0 ) return ret ;
for ( idx = 0 ; idx < BUFFER_COUNT ; idx + + ) {
bp = pvr2_stream_get_buffer ( sp , idx ) ;
pvr2_buffer_set_buffer ( bp ,
cp - > buffer_storage [ idx ] ,
BUFFER_SIZE ) ;
}
cp - > stream = sp ;
}
} while ( 0 ) ; mutex_unlock ( & cp - > mutex ) ;
return 0 ;
}
int pvr2_ioread_set_enabled ( struct pvr2_ioread * cp , int fl )
{
int ret = 0 ;
if ( ( ! fl ) = = ( ! ( cp - > enabled ) ) ) return ret ;
mutex_lock ( & cp - > mutex ) ; do {
if ( fl ) {
ret = pvr2_ioread_start ( cp ) ;
} else {
pvr2_ioread_stop ( cp ) ;
}
} while ( 0 ) ; mutex_unlock ( & cp - > mutex ) ;
return ret ;
}
2006-06-30 11:30:20 -03:00
static int pvr2_ioread_get_buffer ( struct pvr2_ioread * cp )
2006-06-26 20:58:46 -03:00
{
int stat ;
while ( cp - > c_data_len < = cp - > c_data_offs ) {
if ( cp - > c_buf ) {
// Flush out current buffer first.
stat = pvr2_buffer_queue ( cp - > c_buf ) ;
if ( stat < 0 ) {
// Streaming error...
pvr2_trace ( PVR2_TRACE_DATA_FLOW ,
" /*---TRACE_READ---*/ "
" pvr2_ioread_read id=%p "
" queue_error=%d " ,
cp , stat ) ;
pvr2_ioread_stop ( cp ) ;
return 0 ;
}
2006-06-30 11:35:28 -03:00
cp - > c_buf = NULL ;
cp - > c_data_ptr = NULL ;
2006-06-26 20:58:46 -03:00
cp - > c_data_len = 0 ;
cp - > c_data_offs = 0 ;
}
// Now get a freshly filled buffer.
cp - > c_buf = pvr2_stream_get_ready_buffer ( cp - > stream ) ;
if ( ! cp - > c_buf ) break ; // Nothing ready; done.
cp - > c_data_len = pvr2_buffer_get_count ( cp - > c_buf ) ;
if ( ! cp - > c_data_len ) {
// Nothing transferred. Was there an error?
stat = pvr2_buffer_get_status ( cp - > c_buf ) ;
if ( stat < 0 ) {
// Streaming error...
pvr2_trace ( PVR2_TRACE_DATA_FLOW ,
" /*---TRACE_READ---*/ "
" pvr2_ioread_read id=%p "
" buffer_error=%d " ,
cp , stat ) ;
pvr2_ioread_stop ( cp ) ;
// Give up.
return 0 ;
}
// Start over...
continue ;
}
cp - > c_data_offs = 0 ;
cp - > c_data_ptr = cp - > buffer_storage [
pvr2_buffer_get_id ( cp - > c_buf ) ] ;
}
return ! 0 ;
}
2006-06-30 11:30:20 -03:00
static void pvr2_ioread_filter ( struct pvr2_ioread * cp )
2006-06-26 20:58:46 -03:00
{
unsigned int idx ;
if ( ! cp - > enabled ) return ;
if ( cp - > sync_state ! = 1 ) return ;
// Search the stream for our synchronization key. This is made
// complicated by the fact that in order to be honest with
// ourselves here we must search across buffer boundaries...
mutex_lock ( & cp - > mutex ) ; while ( 1 ) {
// Ensure we have a buffer
if ( ! pvr2_ioread_get_buffer ( cp ) ) break ;
if ( ! cp - > c_data_len ) break ;
// Now walk the buffer contents until we match the key or
// run out of buffer data.
for ( idx = cp - > c_data_offs ; idx < cp - > c_data_len ; idx + + ) {
if ( cp - > sync_buf_offs > = cp - > sync_key_len ) break ;
if ( cp - > c_data_ptr [ idx ] = =
cp - > sync_key_ptr [ cp - > sync_buf_offs ] ) {
// Found the next key byte
( cp - > sync_buf_offs ) + + ;
} else {
// Whoops, mismatched. Start key over...
cp - > sync_buf_offs = 0 ;
}
}
// Consume what we've walked through
cp - > c_data_offs + = idx ;
cp - > sync_trashed_count + = idx ;
// If we've found the key, then update state and get out.
if ( cp - > sync_buf_offs > = cp - > sync_key_len ) {
cp - > sync_trashed_count - = cp - > sync_key_len ;
pvr2_trace ( PVR2_TRACE_DATA_FLOW ,
" /*---TRACE_READ---*/ "
" sync_state <== 2 (skipped %u bytes) " ,
cp - > sync_trashed_count ) ;
cp - > sync_state = 2 ;
cp - > sync_buf_offs = 0 ;
break ;
}
if ( cp - > c_data_offs < cp - > c_data_len ) {
// Sanity check - should NEVER get here
pvr2_trace ( PVR2_TRACE_ERROR_LEGS ,
" ERROR: pvr2_ioread filter sync problem "
" len=%u offs=%u " ,
cp - > c_data_len , cp - > c_data_offs ) ;
// Get out so we don't get stuck in an infinite
// loop.
break ;
}
continue ; // (for clarity)
} mutex_unlock ( & cp - > mutex ) ;
}
int pvr2_ioread_avail ( struct pvr2_ioread * cp )
{
int ret ;
if ( ! ( cp - > enabled ) ) {
// Stream is not enabled; so this is an I/O error
return - EIO ;
}
if ( cp - > sync_state = = 1 ) {
pvr2_ioread_filter ( cp ) ;
if ( cp - > sync_state = = 1 ) return - EAGAIN ;
}
ret = 0 ;
if ( cp - > stream_running ) {
if ( ! pvr2_stream_get_ready_count ( cp - > stream ) ) {
// No data available at all right now.
ret = - EAGAIN ;
}
} else {
if ( pvr2_stream_get_ready_count ( cp - > stream ) < BUFFER_COUNT / 2 ) {
// Haven't buffered up enough yet; try again later
ret = - EAGAIN ;
}
}
if ( ( ! ( cp - > spigot_open ) ) ! = ( ! ( ret = = 0 ) ) ) {
cp - > spigot_open = ( ret = = 0 ) ;
pvr2_trace ( PVR2_TRACE_DATA_FLOW ,
" /*---TRACE_READ---*/ data is %s " ,
cp - > spigot_open ? " available " : " pending " ) ;
}
return ret ;
}
int pvr2_ioread_read ( struct pvr2_ioread * cp , void __user * buf , unsigned int cnt )
{
unsigned int copied_cnt ;
unsigned int bcnt ;
const char * src ;
int stat ;
int ret = 0 ;
unsigned int req_cnt = cnt ;
if ( ! cnt ) {
pvr2_trace ( PVR2_TRACE_TRAP ,
" /*---TRACE_READ---*/ pvr2_ioread_read id=%p "
" ZERO Request? Returning zero. " , cp ) ;
return 0 ;
}
stat = pvr2_ioread_avail ( cp ) ;
if ( stat < 0 ) return stat ;
cp - > stream_running = ! 0 ;
mutex_lock ( & cp - > mutex ) ; do {
// Suck data out of the buffers and copy to the user
copied_cnt = 0 ;
if ( ! buf ) cnt = 0 ;
while ( 1 ) {
if ( ! pvr2_ioread_get_buffer ( cp ) ) {
ret = - EIO ;
break ;
}
if ( ! cnt ) break ;
if ( cp - > sync_state = = 2 ) {
// We're repeating the sync key data into
// the stream.
src = cp - > sync_key_ptr + cp - > sync_buf_offs ;
bcnt = cp - > sync_key_len - cp - > sync_buf_offs ;
} else {
// Normal buffer copy
src = cp - > c_data_ptr + cp - > c_data_offs ;
bcnt = cp - > c_data_len - cp - > c_data_offs ;
}
if ( ! bcnt ) break ;
// Don't run past user's buffer
if ( bcnt > cnt ) bcnt = cnt ;
if ( copy_to_user ( buf , src , bcnt ) ) {
// User supplied a bad pointer?
// Give up - this *will* cause data
// to be lost.
ret = - EFAULT ;
break ;
}
cnt - = bcnt ;
buf + = bcnt ;
copied_cnt + = bcnt ;
if ( cp - > sync_state = = 2 ) {
// Update offset inside sync key that we're
// repeating back out.
cp - > sync_buf_offs + = bcnt ;
if ( cp - > sync_buf_offs > = cp - > sync_key_len ) {
// Consumed entire key; switch mode
// to normal.
pvr2_trace ( PVR2_TRACE_DATA_FLOW ,
" /*---TRACE_READ---*/ "
" sync_state <== 0 " ) ;
cp - > sync_state = 0 ;
}
} else {
// Update buffer offset.
cp - > c_data_offs + = bcnt ;
}
}
} while ( 0 ) ; mutex_unlock ( & cp - > mutex ) ;
if ( ! ret ) {
if ( copied_cnt ) {
// If anything was copied, return that count
ret = copied_cnt ;
} else {
// Nothing copied; suggest to caller that another
// attempt should be tried again later
ret = - EAGAIN ;
}
}
pvr2_trace ( PVR2_TRACE_DATA_FLOW ,
" /*---TRACE_READ---*/ pvr2_ioread_read "
" id=%p request=%d result=%d " ,
cp , req_cnt , ret ) ;
return ret ;
}
/*
Stuff for Emacs to see , in order to encourage consistent editing style :
* * * Local Variables : * * *
* * * mode : c * * *
* * * fill - column : 75 * * *
* * * tab - width : 8 * * *
* * * c - basic - offset : 8 * * *
* * * End : * * *
*/