2008-04-29 03:24:33 +04:00
/*
* cx18 file operation functions
*
* Derived from ivtv - fileops . c
*
* Copyright ( C ) 2007 Hans Verkuil < hverkuil @ xs4all . nl >
2010-05-24 01:53:35 +04:00
* Copyright ( C ) 2008 Andy Walls < awalls @ md . metrocast . net >
2008-04-29 03:24:33 +04:00
*
* 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 , or
* ( at your option ) any later version .
*
* 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 "cx18-driver.h"
# include "cx18-fileops.h"
# include "cx18-i2c.h"
# include "cx18-queue.h"
# include "cx18-vbi.h"
# include "cx18-audio.h"
# include "cx18-mailbox.h"
# include "cx18-scb.h"
# include "cx18-streams.h"
# include "cx18-controls.h"
# include "cx18-ioctl.h"
# include "cx18-cards.h"
/* This function tries to claim the stream for a specific file descriptor.
If no one else is using this stream then the stream is claimed and
2009-12-31 23:19:25 +03:00
associated VBI and IDX streams are also automatically claimed .
2008-04-29 03:24:33 +04:00
Possible error returns : - EBUSY if someone else has claimed
the stream or 0 on success . */
2009-11-20 04:52:30 +03:00
int cx18_claim_stream ( struct cx18_open_id * id , int type )
2008-04-29 03:24:33 +04:00
{
struct cx18 * cx = id - > cx ;
struct cx18_stream * s = & cx - > streams [ type ] ;
2009-12-31 23:19:25 +03:00
struct cx18_stream * s_assoc ;
/* Nothing should ever try to directly claim the IDX stream */
if ( type = = CX18_ENC_STREAM_TYPE_IDX ) {
CX18_WARN ( " MPEG Index stream cannot be claimed "
" directly, but something tried. \n " ) ;
return - EINVAL ;
}
2008-04-29 03:24:33 +04:00
if ( test_and_set_bit ( CX18_F_S_CLAIMED , & s - > s_flags ) ) {
/* someone already claimed this stream */
if ( s - > id = = id - > open_id ) {
/* yes, this file descriptor did. So that's OK. */
return 0 ;
}
if ( s - > id = = - 1 & & type = = CX18_ENC_STREAM_TYPE_VBI ) {
/* VBI is handled already internally, now also assign
the file descriptor to this stream for external
reading of the stream . */
s - > id = id - > open_id ;
CX18_DEBUG_INFO ( " Start Read VBI \n " ) ;
return 0 ;
}
/* someone else is using this stream already */
CX18_DEBUG_INFO ( " Stream %d is busy \n " , type ) ;
return - EBUSY ;
}
s - > id = id - > open_id ;
2009-12-31 23:19:25 +03:00
/*
* CX18_ENC_STREAM_TYPE_MPG needs to claim :
* CX18_ENC_STREAM_TYPE_VBI , if VBI insertion is on for sliced VBI , or
* CX18_ENC_STREAM_TYPE_IDX , if VBI insertion is off for sliced VBI
* ( We don ' t yet fix up MPEG Index entries for our inserted packets ) .
*
* For all other streams we ' re done .
*/
if ( type ! = CX18_ENC_STREAM_TYPE_MPG )
return 0 ;
s_assoc = & cx - > streams [ CX18_ENC_STREAM_TYPE_IDX ] ;
if ( cx - > vbi . insert_mpeg & & ! cx18_raw_vbi ( cx ) )
s_assoc = & cx - > streams [ CX18_ENC_STREAM_TYPE_VBI ] ;
else if ( ! cx18_stream_enabled ( s_assoc ) )
2008-04-29 03:24:33 +04:00
return 0 ;
2009-12-31 23:19:25 +03:00
set_bit ( CX18_F_S_CLAIMED , & s_assoc - > s_flags ) ;
2008-04-29 03:24:33 +04:00
/* mark that it is used internally */
2009-12-31 23:19:25 +03:00
set_bit ( CX18_F_S_INTERNAL_USE , & s_assoc - > s_flags ) ;
2008-04-29 03:24:33 +04:00
return 0 ;
}
2009-11-20 04:52:30 +03:00
EXPORT_SYMBOL ( cx18_claim_stream ) ;
2008-04-29 03:24:33 +04:00
/* This function releases a previously claimed stream. It will take into
account associated VBI streams . */
2009-11-20 04:52:30 +03:00
void cx18_release_stream ( struct cx18_stream * s )
2008-04-29 03:24:33 +04:00
{
struct cx18 * cx = s - > cx ;
2009-12-31 23:19:25 +03:00
struct cx18_stream * s_assoc ;
2008-04-29 03:24:33 +04:00
s - > id = - 1 ;
2009-12-31 23:19:25 +03:00
if ( s - > type = = CX18_ENC_STREAM_TYPE_IDX ) {
/*
* The IDX stream is only used internally , and can
* only be indirectly unclaimed by unclaiming the MPG stream .
*/
return ;
}
2008-04-29 03:24:33 +04:00
if ( s - > type = = CX18_ENC_STREAM_TYPE_VBI & &
test_bit ( CX18_F_S_INTERNAL_USE , & s - > s_flags ) ) {
/* this stream is still in use internally */
return ;
}
if ( ! test_and_clear_bit ( CX18_F_S_CLAIMED , & s - > s_flags ) ) {
CX18_DEBUG_WARN ( " Release stream %s not in use! \n " , s - > name ) ;
return ;
}
cx18_flush_queues ( s ) ;
2009-12-31 23:19:25 +03:00
/*
* CX18_ENC_STREAM_TYPE_MPG needs to release the
* CX18_ENC_STREAM_TYPE_VBI and / or CX18_ENC_STREAM_TYPE_IDX streams .
*
* For all other streams we ' re done .
*/
if ( s - > type ! = CX18_ENC_STREAM_TYPE_MPG )
2008-04-29 03:24:33 +04:00
return ;
2009-12-31 23:19:25 +03:00
/* Unclaim the associated MPEG Index stream */
s_assoc = & cx - > streams [ CX18_ENC_STREAM_TYPE_IDX ] ;
if ( test_and_clear_bit ( CX18_F_S_INTERNAL_USE , & s_assoc - > s_flags ) ) {
clear_bit ( CX18_F_S_CLAIMED , & s_assoc - > s_flags ) ;
cx18_flush_queues ( s_assoc ) ;
2008-04-29 03:24:33 +04:00
}
2009-12-31 23:19:25 +03:00
/* Unclaim the associated VBI stream */
s_assoc = & cx - > streams [ CX18_ENC_STREAM_TYPE_VBI ] ;
if ( test_and_clear_bit ( CX18_F_S_INTERNAL_USE , & s_assoc - > s_flags ) ) {
if ( s_assoc - > id = = - 1 ) {
/*
* The VBI stream is not still claimed by a file
* descriptor , so completely unclaim it .
*/
clear_bit ( CX18_F_S_CLAIMED , & s_assoc - > s_flags ) ;
cx18_flush_queues ( s_assoc ) ;
}
2008-04-29 03:24:33 +04:00
}
}
2009-11-20 04:52:30 +03:00
EXPORT_SYMBOL ( cx18_release_stream ) ;
2008-04-29 03:24:33 +04:00
static void cx18_dualwatch ( struct cx18 * cx )
{
struct v4l2_tuner vt ;
2009-01-02 01:02:31 +03:00
u32 new_stereo_mode ;
const u32 dual = 0x0200 ;
2008-04-29 03:24:33 +04:00
2010-12-31 16:22:52 +03:00
new_stereo_mode = v4l2_ctrl_g_ctrl ( cx - > cxhdl . audio_mode ) ;
2008-04-29 03:24:33 +04:00
memset ( & vt , 0 , sizeof ( vt ) ) ;
2009-02-21 05:52:13 +03:00
cx18_call_all ( cx , tuner , g_tuner , & vt ) ;
2008-04-29 03:24:33 +04:00
if ( vt . audmode = = V4L2_TUNER_MODE_LANG1_LANG2 & &
( vt . rxsubchans & V4L2_TUNER_SUB_LANG2 ) )
new_stereo_mode = dual ;
if ( new_stereo_mode = = cx - > dualwatch_stereo_mode )
return ;
2010-12-31 16:22:52 +03:00
CX18_DEBUG_INFO ( " dualwatch: change stereo flag from 0x%x to 0x%x. \n " ,
cx - > dualwatch_stereo_mode , new_stereo_mode ) ;
if ( v4l2_ctrl_s_ctrl ( cx - > cxhdl . audio_mode , new_stereo_mode ) )
CX18_DEBUG_INFO ( " dualwatch: changing stereo flag failed \n " ) ;
2008-04-29 03:24:33 +04:00
}
2009-11-09 05:45:24 +03:00
static struct cx18_mdl * cx18_get_mdl ( struct cx18_stream * s , int non_block ,
int * err )
2008-04-29 03:24:33 +04:00
{
struct cx18 * cx = s - > cx ;
struct cx18_stream * s_vbi = & cx - > streams [ CX18_ENC_STREAM_TYPE_VBI ] ;
2009-11-09 05:45:24 +03:00
struct cx18_mdl * mdl ;
2008-04-29 03:24:33 +04:00
DEFINE_WAIT ( wait ) ;
* err = 0 ;
while ( 1 ) {
if ( s - > type = = CX18_ENC_STREAM_TYPE_MPG ) {
2010-01-01 04:27:28 +03:00
/* Process pending program updates and VBI data */
2008-04-29 03:24:33 +04:00
if ( time_after ( jiffies , cx - > dualwatch_jiffies + msecs_to_jiffies ( 1000 ) ) ) {
cx - > dualwatch_jiffies = jiffies ;
cx18_dualwatch ( cx ) ;
}
if ( test_bit ( CX18_F_S_INTERNAL_USE , & s_vbi - > s_flags ) & &
! test_bit ( CX18_F_S_APPL_IO , & s_vbi - > s_flags ) ) {
2009-11-09 05:45:24 +03:00
while ( ( mdl = cx18_dequeue ( s_vbi ,
& s_vbi - > q_full ) ) ) {
2008-04-29 03:24:33 +04:00
/* byteswap and process VBI data */
2009-11-09 05:45:24 +03:00
cx18_process_vbi_data ( cx , mdl ,
2008-12-13 02:00:29 +03:00
s_vbi - > type ) ;
2009-11-09 05:45:24 +03:00
cx18_stream_put_mdl_fw ( s_vbi , mdl ) ;
2008-04-29 03:24:33 +04:00
}
}
2009-11-09 05:45:24 +03:00
mdl = & cx - > vbi . sliced_mpeg_mdl ;
if ( mdl - > readpos ! = mdl - > bytesused )
return mdl ;
2008-04-29 03:24:33 +04:00
}
/* do we have new data? */
2009-11-09 05:45:24 +03:00
mdl = cx18_dequeue ( s , & s - > q_full ) ;
if ( mdl ) {
if ( ! test_and_clear_bit ( CX18_F_M_NEED_SWAP ,
& mdl - > m_flags ) )
return mdl ;
2008-04-29 03:24:33 +04:00
if ( s - > type = = CX18_ENC_STREAM_TYPE_MPG )
/* byteswap MPG data */
2009-11-09 05:45:24 +03:00
cx18_mdl_swap ( mdl ) ;
2008-04-29 03:24:33 +04:00
else {
/* byteswap and process VBI data */
2009-11-09 05:45:24 +03:00
cx18_process_vbi_data ( cx , mdl , s - > type ) ;
2008-04-29 03:24:33 +04:00
}
2009-11-09 05:45:24 +03:00
return mdl ;
2008-04-29 03:24:33 +04:00
}
/* return if end of stream */
if ( ! test_bit ( CX18_F_S_STREAMING , & s - > s_flags ) ) {
CX18_DEBUG_INFO ( " EOS %s \n " , s - > name ) ;
return NULL ;
}
/* return if file was opened with O_NONBLOCK */
if ( non_block ) {
* err = - EAGAIN ;
return NULL ;
}
/* wait for more data to arrive */
prepare_to_wait ( & s - > waitq , & wait , TASK_INTERRUPTIBLE ) ;
/* New buffers might have become available before we were added
to the waitqueue */
2009-11-05 05:13:58 +03:00
if ( ! atomic_read ( & s - > q_full . depth ) )
2008-04-29 03:24:33 +04:00
schedule ( ) ;
finish_wait ( & s - > waitq , & wait ) ;
if ( signal_pending ( current ) ) {
/* return if a signal was received */
CX18_DEBUG_INFO ( " User stopped %s \n " , s - > name ) ;
* err = - EINTR ;
return NULL ;
}
}
}
2009-11-09 05:45:24 +03:00
static void cx18_setup_sliced_vbi_mdl ( struct cx18 * cx )
2008-04-29 03:24:33 +04:00
{
2009-11-09 05:45:24 +03:00
struct cx18_mdl * mdl = & cx - > vbi . sliced_mpeg_mdl ;
struct cx18_buffer * buf = & cx - > vbi . sliced_mpeg_buf ;
2008-04-29 03:24:33 +04:00
int idx = cx - > vbi . inserted_frame % CX18_VBI_FRAMES ;
2009-11-09 05:45:24 +03:00
buf - > buf = cx - > vbi . sliced_mpeg_data [ idx ] ;
buf - > bytesused = cx - > vbi . sliced_mpeg_size [ idx ] ;
buf - > readpos = 0 ;
mdl - > curr_buf = NULL ;
mdl - > bytesused = cx - > vbi . sliced_mpeg_size [ idx ] ;
mdl - > readpos = 0 ;
2008-04-29 03:24:33 +04:00
}
static size_t cx18_copy_buf_to_user ( struct cx18_stream * s ,
2009-11-09 05:45:24 +03:00
struct cx18_buffer * buf , char __user * ubuf , size_t ucount , bool * stop )
2008-04-29 03:24:33 +04:00
{
struct cx18 * cx = s - > cx ;
size_t len = buf - > bytesused - buf - > readpos ;
2009-11-09 05:45:24 +03:00
* stop = false ;
2008-04-29 03:24:33 +04:00
if ( len > ucount )
len = ucount ;
if ( cx - > vbi . insert_mpeg & & s - > type = = CX18_ENC_STREAM_TYPE_MPG & &
2008-12-12 22:24:04 +03:00
! cx18_raw_vbi ( cx ) & & buf ! = & cx - > vbi . sliced_mpeg_buf ) {
2009-01-31 06:33:02 +03:00
/*
* Try to find a good splice point in the PS , just before
* an MPEG - 2 Program Pack start code , and provide only
* up to that point to the user , so it ' s easy to insert VBI data
* the next time around .
2009-04-26 23:34:36 +04:00
*
* This will not work for an MPEG - 2 TS and has only been
* verified by analysis to work for an MPEG - 2 PS . Helen Buus
* pointed out this works for the CX23416 MPEG - 2 DVD compatible
* stream , and research indicates both the MPEG 2 SVCD and DVD
* stream types use an MPEG - 2 PS container .
2009-01-31 06:33:02 +03:00
*/
/*
* An MPEG - 2 Program Stream ( PS ) is a series of
* MPEG - 2 Program Packs terminated by an
* MPEG Program End Code after the last Program Pack .
* A Program Pack may hold a PS System Header packet and any
* number of Program Elementary Stream ( PES ) Packets
*/
2008-04-29 03:24:33 +04:00
const char * start = buf - > buf + buf - > readpos ;
const char * p = start + 1 ;
const u8 * q ;
u8 ch = cx - > search_pack_header ? 0xba : 0xe0 ;
int stuffing , i ;
while ( start + len > p ) {
2009-01-31 06:33:02 +03:00
/* Scan for a 0 to find a potential MPEG-2 start code */
2008-04-29 03:24:33 +04:00
q = memchr ( p , 0 , start + len - p ) ;
if ( q = = NULL )
break ;
p = q + 1 ;
2009-01-31 06:33:02 +03:00
/*
* Keep looking if not a
* MPEG - 2 Pack header start code : 0x00 0x00 0x01 0xba
* or MPEG - 2 video PES start code : 0x00 0x00 0x01 0xe0
*/
2008-04-29 03:24:33 +04:00
if ( ( char * ) q + 15 > = buf - > buf + buf - > bytesused | |
q [ 1 ] ! = 0 | | q [ 2 ] ! = 1 | | q [ 3 ] ! = ch )
continue ;
2009-01-31 06:33:02 +03:00
/* If expecting the primary video PES */
2008-04-29 03:24:33 +04:00
if ( ! cx - > search_pack_header ) {
2009-01-31 06:33:02 +03:00
/* Continue if it couldn't be a PES packet */
2008-04-29 03:24:33 +04:00
if ( ( q [ 6 ] & 0xc0 ) ! = 0x80 )
continue ;
2009-01-31 06:33:02 +03:00
/* Check if a PTS or PTS & DTS follow */
if ( ( ( q [ 7 ] & 0xc0 ) = = 0x80 & & /* PTS only */
( q [ 9 ] & 0xf0 ) = = 0x20 ) | | /* PTS only */
( ( q [ 7 ] & 0xc0 ) = = 0xc0 & & /* PTS & DTS */
( q [ 9 ] & 0xf0 ) = = 0x30 ) ) { /* DTS follows */
/* Assume we found the video PES hdr */
ch = 0xba ; /* next want a Program Pack*/
2008-04-29 03:24:33 +04:00
cx - > search_pack_header = 1 ;
2009-01-31 06:33:02 +03:00
p = q + 9 ; /* Skip this video PES hdr */
2008-04-29 03:24:33 +04:00
}
continue ;
}
2009-01-31 06:33:02 +03:00
/* We may have found a Program Pack start code */
/* Get the count of stuffing bytes & verify them */
2008-04-29 03:24:33 +04:00
stuffing = q [ 13 ] & 7 ;
/* all stuffing bytes must be 0xff */
for ( i = 0 ; i < stuffing ; i + + )
if ( q [ 14 + i ] ! = 0xff )
break ;
2009-01-31 06:33:02 +03:00
if ( i = = stuffing & & /* right number of stuffing bytes*/
( q [ 4 ] & 0xc4 ) = = 0x44 & & /* marker check */
( q [ 12 ] & 3 ) = = 3 & & /* marker check */
q [ 14 + stuffing ] = = 0 & & /* PES Pack or Sys Hdr */
2008-04-29 03:24:33 +04:00
q [ 15 + stuffing ] = = 0 & &
q [ 16 + stuffing ] = = 1 ) {
2009-01-31 06:33:02 +03:00
/* We declare we actually found a Program Pack*/
cx - > search_pack_header = 0 ; /* expect vid PES */
2008-04-29 03:24:33 +04:00
len = ( char * ) q - start ;
2009-11-09 05:45:24 +03:00
cx18_setup_sliced_vbi_mdl ( cx ) ;
* stop = true ;
2008-04-29 03:24:33 +04:00
break ;
}
}
}
if ( copy_to_user ( ubuf , ( u8 * ) buf - > buf + buf - > readpos , len ) ) {
CX18_DEBUG_WARN ( " copy %zd bytes to user failed for %s \n " ,
len , s - > name ) ;
return - EFAULT ;
}
buf - > readpos + = len ;
if ( s - > type = = CX18_ENC_STREAM_TYPE_MPG & &
buf ! = & cx - > vbi . sliced_mpeg_buf )
cx - > mpg_data_received + = len ;
return len ;
}
2009-11-09 05:45:24 +03:00
static size_t cx18_copy_mdl_to_user ( struct cx18_stream * s ,
struct cx18_mdl * mdl , char __user * ubuf , size_t ucount )
{
size_t tot_written = 0 ;
int rc ;
bool stop = false ;
if ( mdl - > curr_buf = = NULL )
mdl - > curr_buf = list_first_entry ( & mdl - > buf_list ,
struct cx18_buffer , list ) ;
if ( list_entry_is_past_end ( mdl - > curr_buf , & mdl - > buf_list , list ) ) {
/*
* For some reason we ' ve exhausted the buffers , but the MDL
* object still said some data was unread .
* Fix that and bail out .
*/
mdl - > readpos = mdl - > bytesused ;
return 0 ;
}
list_for_each_entry_from ( mdl - > curr_buf , & mdl - > buf_list , list ) {
if ( mdl - > curr_buf - > readpos > = mdl - > curr_buf - > bytesused )
continue ;
rc = cx18_copy_buf_to_user ( s , mdl - > curr_buf , ubuf + tot_written ,
ucount - tot_written , & stop ) ;
if ( rc < 0 )
return rc ;
mdl - > readpos + = rc ;
tot_written + = rc ;
if ( stop | | /* Forced stopping point for VBI insertion */
tot_written > = ucount | | /* Reader request statisfied */
mdl - > curr_buf - > readpos < mdl - > curr_buf - > bytesused | |
mdl - > readpos > = mdl - > bytesused ) /* MDL buffers drained */
break ;
}
return tot_written ;
}
2008-04-29 03:24:33 +04:00
static ssize_t cx18_read ( struct cx18_stream * s , char __user * ubuf ,
size_t tot_count , int non_block )
{
struct cx18 * cx = s - > cx ;
size_t tot_written = 0 ;
int single_frame = 0 ;
2008-05-25 18:21:27 +04:00
if ( atomic_read ( & cx - > ana_capturing ) = = 0 & & s - > id = = - 1 ) {
2008-04-29 03:24:33 +04:00
/* shouldn't happen */
CX18_DEBUG_WARN ( " Stream %s not initialized before read \n " ,
s - > name ) ;
return - EIO ;
}
/* Each VBI buffer is one frame, the v4l2 API says that for VBI the
frames should arrive one - by - one , so make sure we never output more
than one VBI frame at a time */
2008-12-12 22:24:04 +03:00
if ( s - > type = = CX18_ENC_STREAM_TYPE_VBI & & ! cx18_raw_vbi ( cx ) )
2008-04-29 03:24:33 +04:00
single_frame = 1 ;
for ( ; ; ) {
2009-11-09 05:45:24 +03:00
struct cx18_mdl * mdl ;
2008-04-29 03:24:33 +04:00
int rc ;
2009-11-09 05:45:24 +03:00
mdl = cx18_get_mdl ( s , non_block , & rc ) ;
2008-04-29 03:24:33 +04:00
/* if there is no data available... */
2009-11-09 05:45:24 +03:00
if ( mdl = = NULL ) {
2008-04-29 03:24:33 +04:00
/* if we got data, then return that regardless */
if ( tot_written )
break ;
/* EOS condition */
if ( rc = = 0 ) {
clear_bit ( CX18_F_S_STREAMOFF , & s - > s_flags ) ;
clear_bit ( CX18_F_S_APPL_IO , & s - > s_flags ) ;
cx18_release_stream ( s ) ;
}
/* set errno */
return rc ;
}
2009-11-09 05:45:24 +03:00
rc = cx18_copy_mdl_to_user ( s , mdl , ubuf + tot_written ,
2008-04-29 03:24:33 +04:00
tot_count - tot_written ) ;
2009-11-09 05:45:24 +03:00
if ( mdl ! = & cx - > vbi . sliced_mpeg_mdl ) {
if ( mdl - > readpos = = mdl - > bytesused )
cx18_stream_put_mdl_fw ( s , mdl ) ;
2008-12-09 05:02:45 +03:00
else
2009-11-09 05:45:24 +03:00
cx18_push ( s , mdl , & s - > q_full ) ;
} else if ( mdl - > readpos = = mdl - > bytesused ) {
2008-04-29 03:24:33 +04:00
int idx = cx - > vbi . inserted_frame % CX18_VBI_FRAMES ;
cx - > vbi . sliced_mpeg_size [ idx ] = 0 ;
cx - > vbi . inserted_frame + + ;
2009-11-09 05:45:24 +03:00
cx - > vbi_data_inserted + = mdl - > bytesused ;
2008-04-29 03:24:33 +04:00
}
if ( rc < 0 )
return rc ;
tot_written + = rc ;
if ( tot_written = = tot_count | | single_frame )
break ;
}
return tot_written ;
}
static ssize_t cx18_read_pos ( struct cx18_stream * s , char __user * ubuf ,
size_t count , loff_t * pos , int non_block )
{
ssize_t rc = count ? cx18_read ( s , ubuf , count , non_block ) : 0 ;
struct cx18 * cx = s - > cx ;
CX18_DEBUG_HI_FILE ( " read %zd from %s, got %zd \n " , count , s - > name , rc ) ;
if ( rc > 0 )
pos + = rc ;
return rc ;
}
int cx18_start_capture ( struct cx18_open_id * id )
{
struct cx18 * cx = id - > cx ;
struct cx18_stream * s = & cx - > streams [ id - > type ] ;
struct cx18_stream * s_vbi ;
2009-12-31 23:19:25 +03:00
struct cx18_stream * s_idx ;
2008-04-29 03:24:33 +04:00
if ( s - > type = = CX18_ENC_STREAM_TYPE_RAD ) {
/* you cannot read from these stream types. */
return - EPERM ;
}
/* Try to claim this stream. */
if ( cx18_claim_stream ( id , s - > type ) )
return - EBUSY ;
/* If capture is already in progress, then we also have to
do nothing extra . */
if ( test_bit ( CX18_F_S_STREAMOFF , & s - > s_flags ) | |
test_and_set_bit ( CX18_F_S_STREAMING , & s - > s_flags ) ) {
set_bit ( CX18_F_S_APPL_IO , & s - > s_flags ) ;
return 0 ;
}
2009-12-31 23:19:25 +03:00
/* Start associated VBI or IDX stream capture if required */
2008-04-29 03:24:33 +04:00
s_vbi = & cx - > streams [ CX18_ENC_STREAM_TYPE_VBI ] ;
2009-12-31 23:19:25 +03:00
s_idx = & cx - > streams [ CX18_ENC_STREAM_TYPE_IDX ] ;
if ( s - > type = = CX18_ENC_STREAM_TYPE_MPG ) {
/*
* The VBI and IDX streams should have been claimed
* automatically , if for internal use , when the MPG stream was
* claimed . We only need to start these streams capturing .
*/
if ( test_bit ( CX18_F_S_INTERNAL_USE , & s_idx - > s_flags ) & &
! test_and_set_bit ( CX18_F_S_STREAMING , & s_idx - > s_flags ) ) {
if ( cx18_start_v4l2_encode_stream ( s_idx ) ) {
CX18_DEBUG_WARN ( " IDX capture start failed \n " ) ;
clear_bit ( CX18_F_S_STREAMING , & s_idx - > s_flags ) ;
goto start_failed ;
}
CX18_DEBUG_INFO ( " IDX capture started \n " ) ;
}
if ( test_bit ( CX18_F_S_INTERNAL_USE , & s_vbi - > s_flags ) & &
! test_and_set_bit ( CX18_F_S_STREAMING , & s_vbi - > s_flags ) ) {
if ( cx18_start_v4l2_encode_stream ( s_vbi ) ) {
CX18_DEBUG_WARN ( " VBI capture start failed \n " ) ;
clear_bit ( CX18_F_S_STREAMING , & s_vbi - > s_flags ) ;
goto start_failed ;
}
CX18_DEBUG_INFO ( " VBI insertion started \n " ) ;
2008-04-29 03:24:33 +04:00
}
}
/* Tell the card to start capturing */
if ( ! cx18_start_v4l2_encode_stream ( s ) ) {
/* We're done */
set_bit ( CX18_F_S_APPL_IO , & s - > s_flags ) ;
/* Resume a possibly paused encoder */
if ( test_and_clear_bit ( CX18_F_I_ENC_PAUSED , & cx - > i_flags ) )
cx18_vapi ( cx , CX18_CPU_CAPTURE_PAUSE , 1 , s - > handle ) ;
return 0 ;
}
2009-12-31 23:19:25 +03:00
start_failed :
2008-04-29 03:24:33 +04:00
CX18_DEBUG_WARN ( " Failed to start capturing for stream %s \n " , s - > name ) ;
2009-12-31 23:19:25 +03:00
/*
* The associated VBI and IDX streams for internal use are released
* automatically when the MPG stream is released . We only need to stop
* the associated stream .
*/
if ( s - > type = = CX18_ENC_STREAM_TYPE_MPG ) {
/* Stop the IDX stream which is always for internal use */
if ( test_bit ( CX18_F_S_STREAMING , & s_idx - > s_flags ) ) {
cx18_stop_v4l2_encode_stream ( s_idx , 0 ) ;
clear_bit ( CX18_F_S_STREAMING , & s_idx - > s_flags ) ;
}
/* Stop the VBI stream, if only running for internal use */
if ( test_bit ( CX18_F_S_STREAMING , & s_vbi - > s_flags ) & &
! test_bit ( CX18_F_S_APPL_IO , & s_vbi - > s_flags ) ) {
cx18_stop_v4l2_encode_stream ( s_vbi , 0 ) ;
clear_bit ( CX18_F_S_STREAMING , & s_vbi - > s_flags ) ;
}
2008-04-29 03:24:33 +04:00
}
clear_bit ( CX18_F_S_STREAMING , & s - > s_flags ) ;
2009-12-31 23:19:25 +03:00
cx18_release_stream ( s ) ; /* Also releases associated streams */
2008-04-29 03:24:33 +04:00
return - EIO ;
}
ssize_t cx18_v4l2_read ( struct file * filp , char __user * buf , size_t count ,
loff_t * pos )
{
2011-03-12 12:35:33 +03:00
struct cx18_open_id * id = file2id ( filp ) ;
2008-04-29 03:24:33 +04:00
struct cx18 * cx = id - > cx ;
struct cx18_stream * s = & cx - > streams [ id - > type ] ;
int rc ;
CX18_DEBUG_HI_FILE ( " read %zd bytes from %s \n " , count , s - > name ) ;
mutex_lock ( & cx - > serialize_lock ) ;
rc = cx18_start_capture ( id ) ;
mutex_unlock ( & cx - > serialize_lock ) ;
if ( rc )
return rc ;
2011-04-06 15:32:56 +04:00
2011-05-03 15:57:40 +04:00
if ( ( s - > vb_type = = V4L2_BUF_TYPE_VIDEO_CAPTURE ) & &
2011-04-06 15:32:56 +04:00
( id - > type = = CX18_ENC_STREAM_TYPE_YUV ) ) {
2011-05-03 15:57:40 +04:00
return videobuf_read_stream ( & s - > vbuf_q , buf , count , pos , 0 ,
2011-04-06 15:32:56 +04:00
filp - > f_flags & O_NONBLOCK ) ;
}
2008-04-29 03:24:33 +04:00
return cx18_read_pos ( s , buf , count , pos , filp - > f_flags & O_NONBLOCK ) ;
}
unsigned int cx18_v4l2_enc_poll ( struct file * filp , poll_table * wait )
{
2011-03-12 12:35:33 +03:00
struct cx18_open_id * id = file2id ( filp ) ;
2008-04-29 03:24:33 +04:00
struct cx18 * cx = id - > cx ;
struct cx18_stream * s = & cx - > streams [ id - > type ] ;
int eof = test_bit ( CX18_F_S_STREAMOFF , & s - > s_flags ) ;
/* Start a capture if there is none */
if ( ! eof & & ! test_bit ( CX18_F_S_STREAMING , & s - > s_flags ) ) {
int rc ;
mutex_lock ( & cx - > serialize_lock ) ;
rc = cx18_start_capture ( id ) ;
mutex_unlock ( & cx - > serialize_lock ) ;
if ( rc ) {
CX18_DEBUG_INFO ( " Could not start capture for %s (%d) \n " ,
s - > name , rc ) ;
return POLLERR ;
}
CX18_DEBUG_FILE ( " Encoder poll started capture \n " ) ;
}
2011-05-03 15:57:40 +04:00
if ( ( s - > vb_type = = V4L2_BUF_TYPE_VIDEO_CAPTURE ) & &
2011-04-06 15:32:56 +04:00
( id - > type = = CX18_ENC_STREAM_TYPE_YUV ) ) {
2011-05-03 15:57:40 +04:00
int videobuf_poll = videobuf_poll_stream ( filp , & s - > vbuf_q , wait ) ;
if ( eof & & videobuf_poll = = POLLERR )
return POLLHUP ;
else
return videobuf_poll ;
2011-04-06 15:32:56 +04:00
}
2008-04-29 03:24:33 +04:00
/* add stream's waitq to the poll list */
CX18_DEBUG_HI_FILE ( " Encoder poll \n " ) ;
poll_wait ( filp , & s - > waitq , wait ) ;
2009-11-05 05:13:58 +03:00
if ( atomic_read ( & s - > q_full . depth ) )
2008-04-29 03:24:33 +04:00
return POLLIN | POLLRDNORM ;
if ( eof )
return POLLHUP ;
return 0 ;
}
2011-04-06 15:32:56 +04:00
int cx18_v4l2_mmap ( struct file * file , struct vm_area_struct * vma )
{
struct cx18_open_id * id = file - > private_data ;
struct cx18 * cx = id - > cx ;
struct cx18_stream * s = & cx - > streams [ id - > type ] ;
int eof = test_bit ( CX18_F_S_STREAMOFF , & s - > s_flags ) ;
2011-05-03 15:57:40 +04:00
if ( ( s - > vb_type = = V4L2_BUF_TYPE_VIDEO_CAPTURE ) & &
2011-04-06 15:32:56 +04:00
( id - > type = = CX18_ENC_STREAM_TYPE_YUV ) ) {
/* Start a capture if there is none */
if ( ! eof & & ! test_bit ( CX18_F_S_STREAMING , & s - > s_flags ) ) {
int rc ;
mutex_lock ( & cx - > serialize_lock ) ;
rc = cx18_start_capture ( id ) ;
mutex_unlock ( & cx - > serialize_lock ) ;
if ( rc ) {
CX18_DEBUG_INFO (
" Could not start capture for %s (%d) \n " ,
s - > name , rc ) ;
return - EINVAL ;
}
2011-05-03 15:57:40 +04:00
CX18_DEBUG_FILE ( " Encoder mmap started capture \n " ) ;
2011-04-06 15:32:56 +04:00
}
2011-05-03 15:57:40 +04:00
return videobuf_mmap_mapper ( & s - > vbuf_q , vma ) ;
2011-04-06 15:32:56 +04:00
}
return - EINVAL ;
}
void cx18_vb_timeout ( unsigned long data )
{
struct cx18_stream * s = ( struct cx18_stream * ) data ;
struct cx18_videobuf_buffer * buf ;
unsigned long flags ;
/* Return all of the buffers in error state, so the vbi/vid inode
* can return from blocking .
*/
spin_lock_irqsave ( & s - > vb_lock , flags ) ;
while ( ! list_empty ( & s - > vb_capture ) ) {
buf = list_entry ( s - > vb_capture . next ,
struct cx18_videobuf_buffer , vb . queue ) ;
list_del ( & buf - > vb . queue ) ;
buf - > vb . state = VIDEOBUF_ERROR ;
wake_up ( & buf - > vb . done ) ;
}
spin_unlock_irqrestore ( & s - > vb_lock , flags ) ;
}
2008-04-29 03:24:33 +04:00
void cx18_stop_capture ( struct cx18_open_id * id , int gop_end )
{
struct cx18 * cx = id - > cx ;
struct cx18_stream * s = & cx - > streams [ id - > type ] ;
2009-12-31 23:19:25 +03:00
struct cx18_stream * s_vbi = & cx - > streams [ CX18_ENC_STREAM_TYPE_VBI ] ;
struct cx18_stream * s_idx = & cx - > streams [ CX18_ENC_STREAM_TYPE_IDX ] ;
2008-04-29 03:24:33 +04:00
CX18_DEBUG_IOCTL ( " close() of %s \n " , s - > name ) ;
/* 'Unclaim' this stream */
/* Stop capturing */
if ( test_bit ( CX18_F_S_STREAMING , & s - > s_flags ) ) {
CX18_DEBUG_INFO ( " close stopping capture \n " ) ;
2009-12-31 23:19:25 +03:00
if ( id - > type = = CX18_ENC_STREAM_TYPE_MPG ) {
/* Stop internal use associated VBI and IDX streams */
if ( test_bit ( CX18_F_S_STREAMING , & s_vbi - > s_flags ) & &
! test_bit ( CX18_F_S_APPL_IO , & s_vbi - > s_flags ) ) {
CX18_DEBUG_INFO ( " close stopping embedded VBI "
" capture \n " ) ;
cx18_stop_v4l2_encode_stream ( s_vbi , 0 ) ;
}
if ( test_bit ( CX18_F_S_STREAMING , & s_idx - > s_flags ) ) {
CX18_DEBUG_INFO ( " close stopping IDX capture \n " ) ;
cx18_stop_v4l2_encode_stream ( s_idx , 0 ) ;
}
2008-04-29 03:24:33 +04:00
}
if ( id - > type = = CX18_ENC_STREAM_TYPE_VBI & &
test_bit ( CX18_F_S_INTERNAL_USE , & s - > s_flags ) )
/* Also used internally, don't stop capturing */
s - > id = - 1 ;
else
cx18_stop_v4l2_encode_stream ( s , gop_end ) ;
}
if ( ! gop_end ) {
clear_bit ( CX18_F_S_APPL_IO , & s - > s_flags ) ;
clear_bit ( CX18_F_S_STREAMOFF , & s - > s_flags ) ;
cx18_release_stream ( s ) ;
}
}
2008-12-30 12:58:20 +03:00
int cx18_v4l2_close ( struct file * filp )
2008-04-29 03:24:33 +04:00
{
2011-03-12 12:35:33 +03:00
struct v4l2_fh * fh = filp - > private_data ;
struct cx18_open_id * id = fh2id ( fh ) ;
2008-04-29 03:24:33 +04:00
struct cx18 * cx = id - > cx ;
struct cx18_stream * s = & cx - > streams [ id - > type ] ;
CX18_DEBUG_IOCTL ( " close() of %s \n " , s - > name ) ;
2011-03-12 12:35:33 +03:00
v4l2_fh_del ( fh ) ;
v4l2_fh_exit ( fh ) ;
2008-04-29 03:24:33 +04:00
/* Easy case first: this stream was never claimed by us */
if ( s - > id ! = id - > open_id ) {
kfree ( id ) ;
return 0 ;
}
/* 'Unclaim' this stream */
/* Stop radio */
mutex_lock ( & cx - > serialize_lock ) ;
if ( id - > type = = CX18_ENC_STREAM_TYPE_RAD ) {
/* Closing radio device, return to TV mode */
cx18_mute ( cx ) ;
/* Mark that the radio is no longer in use */
clear_bit ( CX18_F_I_RADIO_USER , & cx - > i_flags ) ;
/* Switch tuner to TV */
2009-04-01 10:52:39 +04:00
cx18_call_all ( cx , core , s_std , cx - > std ) ;
2008-04-29 03:24:33 +04:00
/* Select correct audio input (i.e. TV tuner or Line in) */
cx18_audio_set_io ( cx ) ;
2008-05-25 18:21:27 +04:00
if ( atomic_read ( & cx - > ana_capturing ) > 0 ) {
2008-04-29 03:24:33 +04:00
/* Undo video mute */
cx18_vapi ( cx , CX18_CPU_SET_VIDEO_MUTE , 2 , s - > handle ,
2010-12-31 16:22:52 +03:00
( v4l2_ctrl_g_ctrl ( cx - > cxhdl . video_mute ) |
( v4l2_ctrl_g_ctrl ( cx - > cxhdl . video_mute_yuv ) < < 8 ) ) ) ;
2008-04-29 03:24:33 +04:00
}
/* Done! Unmute and continue. */
cx18_unmute ( cx ) ;
cx18_release_stream ( s ) ;
} else {
cx18_stop_capture ( id , 0 ) ;
}
kfree ( id ) ;
mutex_unlock ( & cx - > serialize_lock ) ;
return 0 ;
}
static int cx18_serialized_open ( struct cx18_stream * s , struct file * filp )
{
struct cx18 * cx = s - > cx ;
struct cx18_open_id * item ;
CX18_DEBUG_FILE ( " open %s \n " , s - > name ) ;
/* Allocate memory */
2011-03-12 12:35:33 +03:00
item = kzalloc ( sizeof ( struct cx18_open_id ) , GFP_KERNEL ) ;
2008-04-29 03:24:33 +04:00
if ( NULL = = item ) {
CX18_DEBUG_WARN ( " nomem on v4l2 open \n " ) ;
return - ENOMEM ;
}
2011-03-12 12:35:33 +03:00
v4l2_fh_init ( & item - > fh , s - > video_dev ) ;
2008-04-29 03:24:33 +04:00
item - > cx = cx ;
item - > type = s - > type ;
item - > open_id = cx - > open_id + + ;
2011-03-12 12:35:33 +03:00
filp - > private_data = & item - > fh ;
2008-04-29 03:24:33 +04:00
if ( item - > type = = CX18_ENC_STREAM_TYPE_RAD ) {
/* Try to claim this stream */
if ( cx18_claim_stream ( item , item - > type ) ) {
/* No, it's already in use */
2011-03-12 12:35:33 +03:00
v4l2_fh_exit ( & item - > fh ) ;
2008-04-29 03:24:33 +04:00
kfree ( item ) ;
return - EBUSY ;
}
if ( ! test_bit ( CX18_F_I_RADIO_USER , & cx - > i_flags ) ) {
2008-05-25 18:21:27 +04:00
if ( atomic_read ( & cx - > ana_capturing ) > 0 ) {
2008-04-29 03:24:33 +04:00
/* switching to radio while capture is
in progress is not polite */
cx18_release_stream ( s ) ;
2011-03-12 12:35:33 +03:00
v4l2_fh_exit ( & item - > fh ) ;
2008-04-29 03:24:33 +04:00
kfree ( item ) ;
return - EBUSY ;
}
}
/* Mark that the radio is being used. */
set_bit ( CX18_F_I_RADIO_USER , & cx - > i_flags ) ;
/* We have the radio */
cx18_mute ( cx ) ;
/* Switch tuner to radio */
2009-02-21 05:52:13 +03:00
cx18_call_all ( cx , tuner , s_radio ) ;
2008-04-29 03:24:33 +04:00
/* Select the correct audio input (i.e. radio tuner) */
cx18_audio_set_io ( cx ) ;
/* Done! Unmute and continue. */
cx18_unmute ( cx ) ;
}
2011-03-12 12:35:33 +03:00
v4l2_fh_add ( & item - > fh ) ;
2008-04-29 03:24:33 +04:00
return 0 ;
}
2008-12-30 12:58:20 +03:00
int cx18_v4l2_open ( struct file * filp )
2008-04-29 03:24:33 +04:00
{
2009-02-14 23:08:37 +03:00
int res ;
struct video_device * video_dev = video_devdata ( filp ) ;
struct cx18_stream * s = video_get_drvdata ( video_dev ) ;
2009-07-02 20:52:46 +04:00
struct cx18 * cx = s - > cx ;
2008-04-29 03:24:33 +04:00
mutex_lock ( & cx - > serialize_lock ) ;
if ( cx18_init_on_first_open ( cx ) ) {
2009-12-10 16:47:13 +03:00
CX18_ERR ( " Failed to initialize on %s \n " ,
video_device_node_name ( video_dev ) ) ;
2008-04-29 03:24:33 +04:00
mutex_unlock ( & cx - > serialize_lock ) ;
return - ENXIO ;
}
res = cx18_serialized_open ( s , filp ) ;
mutex_unlock ( & cx - > serialize_lock ) ;
return res ;
}
void cx18_mute ( struct cx18 * cx )
{
2008-08-23 23:42:29 +04:00
u32 h ;
if ( atomic_read ( & cx - > ana_capturing ) ) {
h = cx18_find_handle ( cx ) ;
if ( h ! = CX18_INVALID_TASK_HANDLE )
cx18_vapi ( cx , CX18_CPU_SET_AUDIO_MUTE , 2 , h , 1 ) ;
else
CX18_ERR ( " Can't find valid task handle for mute \n " ) ;
}
2008-04-29 03:24:33 +04:00
CX18_DEBUG_INFO ( " Mute \n " ) ;
}
void cx18_unmute ( struct cx18 * cx )
{
2008-08-23 23:42:29 +04:00
u32 h ;
2008-05-25 18:21:27 +04:00
if ( atomic_read ( & cx - > ana_capturing ) ) {
2008-08-23 23:42:29 +04:00
h = cx18_find_handle ( cx ) ;
if ( h ! = CX18_INVALID_TASK_HANDLE ) {
cx18_msleep_timeout ( 100 , 0 ) ;
cx18_vapi ( cx , CX18_CPU_SET_MISC_PARAMETERS , 2 , h , 12 ) ;
cx18_vapi ( cx , CX18_CPU_SET_AUDIO_MUTE , 2 , h , 0 ) ;
} else
CX18_ERR ( " Can't find valid task handle for unmute \n " ) ;
2008-04-29 03:24:33 +04:00
}
CX18_DEBUG_INFO ( " Unmute \n " ) ;
}